@strapi/admin 4.1.1 → 4.1.4-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/admin/src/assets/images/icon_made-by-strapi.svg +5 -0
  2. package/admin/src/components/AuthenticatedApp/index.js +9 -2
  3. package/admin/src/components/AuthenticatedApp/utils/api.js +20 -1
  4. package/admin/src/components/AutoReloadOverlayBlockerProvider/Blocker.js +2 -2
  5. package/admin/src/components/GuidedTour/Homepage/index.js +1 -1
  6. package/admin/src/components/GuidedTour/Modal/components/Content.js +1 -1
  7. package/admin/src/components/GuidedTour/Modal/components/Modal.js +1 -1
  8. package/admin/src/components/LeftMenu/index.js +1 -1
  9. package/admin/src/components/Notifications/index.js +1 -1
  10. package/admin/src/{pages/AuthPage/components/Logo → components/UnauthenticatedLogo}/index.js +1 -1
  11. package/admin/src/components/UpgradePlanModal/index.js +2 -2
  12. package/admin/src/content-manager/components/CollectionTypeFormWrapper/index.js +4 -4
  13. package/admin/src/content-manager/components/ComponentInitializer/index.js +1 -1
  14. package/admin/src/content-manager/components/DynamicTable/ConfirmDialogDelete/index.js +1 -1
  15. package/admin/src/content-manager/components/DynamicTable/ConfirmDialogDeleteAll/index.js +1 -1
  16. package/admin/src/content-manager/components/DynamicZone/components/Component/index.js +2 -2
  17. package/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/ComponentCard/index.js +1 -1
  18. package/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/index.js +5 -2
  19. package/admin/src/content-manager/components/DynamicZone/index.js +2 -5
  20. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/cleanData.js +1 -6
  21. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/schema.js +0 -9
  22. package/admin/src/content-manager/components/FieldComponent/index.js +2 -5
  23. package/admin/src/content-manager/components/InputJSON/index.js +2 -5
  24. package/admin/src/content-manager/components/InputUID/index.js +1 -2
  25. package/admin/src/content-manager/components/NonRepeatableComponent/index.js +1 -1
  26. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/DraggingSibling.js +2 -2
  27. package/admin/src/content-manager/components/RepeatableComponent/DraggedItem/index.js +4 -7
  28. package/admin/src/content-manager/components/SelectMany/index.js +2 -2
  29. package/admin/src/content-manager/components/SelectOne/index.js +1 -1
  30. package/admin/src/content-manager/components/SelectWrapper/index.js +2 -5
  31. package/admin/src/content-manager/components/SingleTypeFormWrapper/index.js +4 -4
  32. package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +1 -1
  33. package/admin/src/content-manager/components/Wysiwyg/WysiwygNav.js +2 -2
  34. package/admin/src/content-manager/components/Wysiwyg/index.js +2 -2
  35. package/admin/src/content-manager/pages/EditSettingsView/components/DisplayedFields.js +2 -2
  36. package/admin/src/content-manager/pages/EditSettingsView/components/DynamicZoneList.js +1 -1
  37. package/admin/src/content-manager/pages/EditSettingsView/components/RelationalFields.js +2 -2
  38. package/admin/src/content-manager/pages/EditSettingsView/index.js +1 -1
  39. package/admin/src/content-manager/pages/EditView/Header/index.js +4 -7
  40. package/admin/src/content-manager/pages/EditView/Informations/index.js +1 -1
  41. package/admin/src/content-manager/pages/EditView/index.js +5 -193
  42. package/admin/src/content-manager/pages/ListSettingsView/components/CardPreview.js +1 -1
  43. package/admin/src/content-manager/pages/ListSettingsView/components/DraggableCard.js +1 -1
  44. package/admin/src/content-manager/pages/ListSettingsView/components/SortDisplayedFields.js +1 -1
  45. package/admin/src/hooks/index.js +0 -1
  46. package/admin/src/hooks/useFetchInstalledPlugins/index.js +23 -0
  47. package/admin/src/{pages/InstalledPluginsPage → hooks/useFetchInstalledPlugins}/utils/api.js +2 -4
  48. package/admin/src/hooks/useFetchMarketplacePlugins/index.js +23 -0
  49. package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +17 -0
  50. package/admin/src/hooks/useSettingsMenu/init.js +2 -2
  51. package/admin/src/pages/Admin/index.js +1 -3
  52. package/admin/src/pages/App/index.js +2 -0
  53. package/admin/src/pages/AuthPage/components/ForgotPassword/index.js +2 -2
  54. package/admin/src/pages/AuthPage/components/ForgotPasswordSuccess/index.js +1 -1
  55. package/admin/src/pages/AuthPage/components/Login/BaseLogin.js +2 -2
  56. package/admin/src/pages/AuthPage/components/Oops/index.js +1 -1
  57. package/admin/src/pages/AuthPage/components/Register/index.js +224 -223
  58. package/admin/src/pages/AuthPage/components/ResetPassword/index.js +2 -2
  59. package/admin/src/pages/AuthPage/index.js +20 -17
  60. package/admin/src/pages/HomePage/ContentBlocks.js +1 -1
  61. package/admin/src/pages/HomePage/HomeHeader.js +1 -1
  62. package/admin/src/pages/HomePage/SocialLinks.js +2 -2
  63. package/admin/src/pages/InstalledPluginsPage/Plugins.js +6 -15
  64. package/admin/src/pages/MarketplacePage/components/EmptyPluginSearch/EmptyPluginGrid.js +27 -0
  65. package/admin/src/pages/MarketplacePage/components/EmptyPluginSearch/index.js +30 -0
  66. package/admin/src/pages/MarketplacePage/components/PluginCard/index.js +186 -0
  67. package/admin/src/pages/MarketplacePage/index.js +198 -100
  68. package/admin/src/pages/MarketplacePage/utils/api.js +9 -0
  69. package/admin/src/pages/ProfilePage/index.js +5 -5
  70. package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +4 -1
  71. package/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +2 -2
  72. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +1 -1
  73. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/index.js +1 -1
  74. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/index.js +2 -2
  75. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/Row/index.js +1 -1
  76. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/RoleForm/index.js +1 -1
  77. package/admin/src/pages/SettingsPage/pages/Roles/EditPage/index.js +2 -2
  78. package/admin/src/pages/SettingsPage/pages/Users/EditPage/index.js +3 -3
  79. package/admin/src/pages/SettingsPage/pages/Users/ListPage/ModalForm/index.js +2 -2
  80. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/EventInput/index.js +1 -1
  81. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/HeadersInput/index.js +1 -1
  82. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/TriggerContainer/index.js +4 -4
  83. package/admin/src/pages/SettingsPage/pages/Webhooks/EditView/components/WebhookForm/index.js +3 -3
  84. package/admin/src/pages/SettingsPage/pages/Webhooks/ListView/index.js +1 -1
  85. package/admin/src/pages/UseCasePage/index.js +173 -0
  86. package/admin/src/translations/en.json +26 -13
  87. package/build/1094.e1db3a1a.chunk.js +1 -0
  88. package/build/1454.f065d92a.chunk.js +2 -0
  89. package/build/{5032.ed02a466.chunk.js.LICENSE.txt → 1454.f065d92a.chunk.js.LICENSE.txt} +0 -0
  90. package/build/1856.a30bd09b.chunk.js +1 -0
  91. package/build/2481.4eae9408.chunk.js +1 -0
  92. package/build/{2912.38fb9bd1.chunk.js → 2912.c5f76e65.chunk.js} +1 -1
  93. package/build/4362.5c92d240.chunk.js +1 -0
  94. package/build/4715.8a2db02a.chunk.js +1 -0
  95. package/build/{4800.18e59c83.chunk.js → 4800.f4a1384a.chunk.js} +1 -1
  96. package/build/497.3fcf6196.chunk.js +1 -0
  97. package/build/4982.601f6196.chunk.js +1 -0
  98. package/build/6250.836851ca.chunk.js +1 -0
  99. package/build/{7841.ef9bcee9.chunk.js → 7841.490dbbf1.chunk.js} +1 -1
  100. package/build/{8042.9b85175a.chunk.js → 8042.1d66811a.chunk.js} +2 -2
  101. package/build/{8042.9b85175a.chunk.js.LICENSE.txt → 8042.1d66811a.chunk.js.LICENSE.txt} +0 -0
  102. package/build/849.17f011e8.chunk.js +1 -0
  103. package/build/90f49a385afb000fb1d4.svg +5 -0
  104. package/build/{2736.ee6e45c9.chunk.js → 9260.fa40c7bd.chunk.js} +2 -2
  105. package/build/{2736.ee6e45c9.chunk.js.LICENSE.txt → 9260.fa40c7bd.chunk.js.LICENSE.txt} +0 -0
  106. package/build/9853.6ff595fa.chunk.js +1 -0
  107. package/build/{9988.b4229043.chunk.js → 9988.fe838ba6.chunk.js} +2 -2
  108. package/build/{9988.b4229043.chunk.js.LICENSE.txt → 9988.fe838ba6.chunk.js.LICENSE.txt} +0 -0
  109. package/build/Admin-authenticatedApp.27fba46d.chunk.js +1 -0
  110. package/build/Admin_homePage.964ff5d7.chunk.js +1 -0
  111. package/build/Admin_marketplace.e83567ff.chunk.js +1 -0
  112. package/build/Admin_pluginsPage.97a514db.chunk.js +1 -0
  113. package/build/Admin_profilePage.c497b39d.chunk.js +1 -0
  114. package/build/Admin_settingsPage.55ec1f30.chunk.js +1 -0
  115. package/build/admin-edit-roles-page.49b6f01d.chunk.js +1 -0
  116. package/build/admin-edit-users.381e4a0d.chunk.js +1 -0
  117. package/build/admin-users.2740c223.chunk.js +1 -0
  118. package/build/{api-tokens-create-page.0981141a.chunk.js → api-tokens-create-page.db17bb39.chunk.js} +1 -1
  119. package/build/{api-tokens-edit-page.3faf1af1.chunk.js → api-tokens-edit-page.c7299a77.chunk.js} +1 -1
  120. package/build/content-manager.e1189026.chunk.js +1 -0
  121. package/build/content-type-builder-translation-en-json.8034dab6.chunk.js +1 -0
  122. package/build/content-type-builder.de5d18ad.chunk.js +1 -0
  123. package/build/{email-settings-page.4338588d.chunk.js → email-settings-page.27ee4a98.chunk.js} +1 -1
  124. package/build/en-json.086acf41.chunk.js +1 -0
  125. package/build/i18n-settings-page.c4018651.chunk.js +1 -0
  126. package/build/i18n-translation-de-json.c5c9054f.chunk.js +1 -0
  127. package/build/index.html +1 -1
  128. package/build/main.22e9a5c5.js +2 -0
  129. package/build/{main.fc123ed7.js.LICENSE.txt → main.22e9a5c5.js.LICENSE.txt} +0 -0
  130. package/build/runtime~main.e6326927.js +1 -0
  131. package/build/{sso-settings-page.c073b6d7.chunk.js → sso-settings-page.121dd0a6.chunk.js} +1 -1
  132. package/build/{upload-settings.8e7cbc3b.chunk.js → upload-settings.4401f36d.chunk.js} +1 -1
  133. package/build/{upload.803ab265.chunk.js → upload.5a2dded7.chunk.js} +1 -1
  134. package/build/{users-advanced-settings-page.7694d3c9.chunk.js → users-advanced-settings-page.8905d8d8.chunk.js} +1 -1
  135. package/build/users-permissions-translation-en-json.21b0fd2f.chunk.js +1 -0
  136. package/build/users-providers-settings-page.368893ed.chunk.js +1 -0
  137. package/build/users-roles-settings-page.a2f6277a.chunk.js +1 -0
  138. package/build/webhook-edit-page.d170eda1.chunk.js +1 -0
  139. package/build/{webhook-list-page.5c8f2a91.chunk.js → webhook-list-page.c21b5a9a.chunk.js} +1 -1
  140. package/ee/admin/pages/AuthPage/components/Login/index.js +1 -1
  141. package/ee/admin/pages/AuthPage/components/Providers/index.js +2 -2
  142. package/ee/admin/pages/SettingsPage/SingleSignOn/index.js +1 -1
  143. package/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/index.js +3 -3
  144. package/index.js +6 -1
  145. package/package.json +7 -7
  146. package/server/controllers/admin.js +12 -1
  147. package/server/routes/serve-admin-panel.js +18 -6
  148. package/.env +0 -0
  149. package/admin/src/content-manager/components/EditViewDataManagerProvider/utils/isValidJSONString.js +0 -15
  150. package/admin/src/hooks/useFetchPluginsFromMarketPlace/index.js +0 -49
  151. package/admin/src/pages/MarketplacePage/MarketplaceBanner/Wrapper.js +0 -28
  152. package/admin/src/pages/MarketplacePage/MarketplaceBanner/index.js +0 -37
  153. package/admin/src/pages/MarketplacePage/PluginCard/Wrapper.js +0 -148
  154. package/admin/src/pages/MarketplacePage/PluginCard/index.js +0 -185
  155. package/admin/src/pages/MarketplacePage/Wrapper.js +0 -5
  156. package/admin/src/pages/MarketplacePage/assets/marketplace-coming-soon.png +0 -0
  157. package/build/01a600d9e6e0dea21e33.png +0 -0
  158. package/build/1856.a06395b4.chunk.js +0 -1
  159. package/build/2481.7d15bd79.chunk.js +0 -1
  160. package/build/4261.a4e1e93c.chunk.js +0 -1
  161. package/build/4362.d0c1a04a.chunk.js +0 -1
  162. package/build/4715.31ca1967.chunk.js +0 -1
  163. package/build/497.8f30da61.chunk.js +0 -1
  164. package/build/4982.da4adb38.chunk.js +0 -1
  165. package/build/5032.ed02a466.chunk.js +0 -2
  166. package/build/6250.dc6d7a58.chunk.js +0 -1
  167. package/build/849.9075d399.chunk.js +0 -1
  168. package/build/9238.bdd93dae.chunk.js +0 -1
  169. package/build/Admin-authenticatedApp.013e2774.chunk.js +0 -1
  170. package/build/Admin_homePage.e4779166.chunk.js +0 -1
  171. package/build/Admin_marketplace.e8654056.chunk.js +0 -1
  172. package/build/Admin_pluginsPage.7d1bd7ce.chunk.js +0 -1
  173. package/build/Admin_profilePage.67dd744c.chunk.js +0 -1
  174. package/build/Admin_settingsPage.2d0d2cca.chunk.js +0 -1
  175. package/build/admin-edit-roles-page.2d1b6461.chunk.js +0 -1
  176. package/build/admin-edit-users.e736db15.chunk.js +0 -1
  177. package/build/admin-users.5f79c031.chunk.js +0 -1
  178. package/build/content-manager.141d110d.chunk.js +0 -1
  179. package/build/content-type-builder-translation-en-json.b3d8e9d4.chunk.js +0 -1
  180. package/build/content-type-builder.f1cef05c.chunk.js +0 -1
  181. package/build/en-json.bb614bb0.chunk.js +0 -1
  182. package/build/i18n-settings-page.51e37957.chunk.js +0 -1
  183. package/build/main.fc123ed7.js +0 -2
  184. package/build/runtime~main.0866074a.js +0 -1
  185. package/build/users-permissions-translation-en-json.1993655e.chunk.js +0 -1
  186. package/build/users-providers-settings-page.47f97b06.chunk.js +0 -1
  187. package/build/users-roles-settings-page.b67e2b4d.chunk.js +0 -1
  188. package/build/webhook-edit-page.adad0a42.chunk.js +0 -1
@@ -1,26 +1,24 @@
1
1
  import React from 'react';
2
- import { useQuery } from 'react-query';
3
2
  import { useIntl } from 'react-intl';
4
- import { LoadingIndicatorPage, useNotification, useFocusWhenNavigate } from '@strapi/helper-plugin';
3
+ import { LoadingIndicatorPage, useFocusWhenNavigate } from '@strapi/helper-plugin';
4
+ import { useNotifyAT } from '@strapi/design-system/LiveRegions';
5
5
  import { Layout, HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
6
6
  import { Main } from '@strapi/design-system/Main';
7
- import { useNotifyAT } from '@strapi/design-system/LiveRegions';
8
7
  import { Typography } from '@strapi/design-system/Typography';
9
8
  import { Table, Thead, Tbody, Tr, Td, Th } from '@strapi/design-system/Table';
10
- import { fetchPlugins } from './utils/api';
9
+ import useFetchInstalledPlugins from '../../hooks/useFetchInstalledPlugins';
11
10
 
12
11
  const Plugins = () => {
13
12
  const { formatMessage } = useIntl();
14
- useFocusWhenNavigate();
15
13
  const { notifyStatus } = useNotifyAT();
16
- const toggleNotification = useNotification();
14
+ useFocusWhenNavigate();
17
15
 
18
16
  const title = formatMessage({
19
17
  id: 'app.components.ListPluginsPage.title',
20
18
  defaultMessage: 'Plugins',
21
19
  });
22
20
 
23
- const notifyLoad = () => {
21
+ const notifyPluginPageLoad = () => {
24
22
  notifyStatus(
25
23
  formatMessage(
26
24
  {
@@ -32,14 +30,7 @@ const Plugins = () => {
32
30
  );
33
31
  };
34
32
 
35
- const { status, data } = useQuery('list-plugins', () => fetchPlugins(notifyLoad), {
36
- onError: () => {
37
- toggleNotification({
38
- type: 'warning',
39
- message: { id: 'notification.error', defaultMessage: 'An error occured' },
40
- });
41
- },
42
- });
33
+ const { status, data } = useFetchInstalledPlugins(notifyPluginPageLoad);
43
34
 
44
35
  const isLoading = status !== 'success' && status !== 'error';
45
36
 
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { Box } from '@strapi/design-system/Box';
4
+ import { GridLayout } from '@strapi/design-system/Layout';
5
+
6
+ const EmptyPluginCard = styled(Box)`
7
+ background: ${({ theme }) =>
8
+ `linear-gradient(180deg, rgba(234, 234, 239, 0) 0%, ${theme.colors.neutral150} 100%)`};
9
+ opacity: 0.33;
10
+ `;
11
+
12
+ export const EmptyPluginGrid = () => {
13
+ return (
14
+ <GridLayout>
15
+ {Array(12)
16
+ .fill(null)
17
+ .map((_, idx) => (
18
+ <EmptyPluginCard
19
+ // eslint-disable-next-line react/no-array-index-key
20
+ key={`empty-plugin-card-${idx}`}
21
+ height="234px"
22
+ hasRadius
23
+ />
24
+ ))}
25
+ </GridLayout>
26
+ );
27
+ };
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Typography } from '@strapi/design-system/Typography';
4
+ import { Box } from '@strapi/design-system/Box';
5
+ import { Flex } from '@strapi/design-system/Flex';
6
+ import { Icon } from '@strapi/design-system/Icon';
7
+ import EmptyStateDocument from '@strapi/icons/EmptyDocuments';
8
+ import { EmptyPluginGrid } from './EmptyPluginGrid';
9
+
10
+ export const EmptyPluginSearch = ({ content }) => {
11
+ return (
12
+ <Box position="relative">
13
+ <EmptyPluginGrid />
14
+ <Box position="absolute" top={11} width="100%">
15
+ <Flex alignItems="center" justifyContent="center" direction="column">
16
+ <Icon as={EmptyStateDocument} color="" width="160px" height="88px" />
17
+ <Box paddingTop={6}>
18
+ <Typography variant="delta" as="p" textColor="neutral600">
19
+ {content}
20
+ </Typography>
21
+ </Box>
22
+ </Flex>
23
+ </Box>
24
+ </Box>
25
+ );
26
+ };
27
+
28
+ EmptyPluginSearch.propTypes = {
29
+ content: PropTypes.string.isRequired,
30
+ };
@@ -0,0 +1,186 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import styled from 'styled-components';
5
+ import { Box } from '@strapi/design-system/Box';
6
+ import { Stack } from '@strapi/design-system/Stack';
7
+ import { Typography } from '@strapi/design-system/Typography';
8
+ import { Button } from '@strapi/design-system/Button';
9
+ import { LinkButton } from '@strapi/design-system/LinkButton';
10
+ import { Flex } from '@strapi/design-system/Flex';
11
+ import { Icon } from '@strapi/design-system/Icon';
12
+ import { Tooltip } from '@strapi/design-system/Tooltip';
13
+ import ExternalLink from '@strapi/icons/ExternalLink';
14
+ import Duplicate from '@strapi/icons/Duplicate';
15
+ import Check from '@strapi/icons/Check';
16
+ import CheckCircle from '@strapi/icons/CheckCircle';
17
+ import { useNotification, useTracking } from '@strapi/helper-plugin';
18
+ import { CopyToClipboard } from 'react-copy-to-clipboard';
19
+ import madeByStrapiIcon from '../../../../assets/images/icon_made-by-strapi.svg';
20
+
21
+ // Custom component to have an ellipsis after the 2nd line
22
+ const EllipsisText = styled(Typography)`
23
+ /* stylelint-disable value-no-vendor-prefix, property-no-vendor-prefix */
24
+ display: -webkit-box;
25
+ -webkit-box-orient: vertical;
26
+ -webkit-line-clamp: 2;
27
+ /* stylelint-enable value-no-vendor-prefix, property-no-vendor-prefix */
28
+ overflow: hidden;
29
+ `;
30
+
31
+ const PluginCard = ({ plugin, installedPluginNames, useYarn }) => {
32
+ const { attributes } = plugin;
33
+ const { formatMessage } = useIntl();
34
+ const toggleNotification = useNotification();
35
+ const { trackUsage } = useTracking();
36
+
37
+ const isInstalled = installedPluginNames.includes(attributes.npmPackageName);
38
+
39
+ const commandToCopy = useYarn
40
+ ? `yarn add ${attributes.npmPackageName}`
41
+ : `npm install ${attributes.npmPackageName}`;
42
+
43
+ const madeByStrapiMessage = formatMessage({
44
+ id: 'admin.pages.MarketPlacePage.plugin.tooltip.madeByStrapi',
45
+ defaultMessage: 'Made by Strapi',
46
+ });
47
+
48
+ return (
49
+ <Flex
50
+ direction="column"
51
+ justifyContent="space-between"
52
+ paddingTop={4}
53
+ paddingRight={6}
54
+ paddingBottom={4}
55
+ paddingLeft={6}
56
+ hasRadius
57
+ background="neutral0"
58
+ shadow="tableShadow"
59
+ height="100%"
60
+ alignItems="normal"
61
+ >
62
+ <Box>
63
+ <Box
64
+ as="img"
65
+ src={attributes.logo.url}
66
+ alt={`${attributes.name} logo`}
67
+ hasRadius
68
+ width={11}
69
+ height={11}
70
+ />
71
+ <Box paddingTop={5}>
72
+ <Typography as="h3" variant="delta">
73
+ <Flex alignItems="center">
74
+ {attributes.name}
75
+ {attributes.validated && !attributes.madeByStrapi && (
76
+ <Tooltip
77
+ description={formatMessage({
78
+ id: 'admin.pages.MarketPlacePage.plugin.tooltip.verified',
79
+ defaultMessage: 'Plugin verified by Strapi',
80
+ })}
81
+ >
82
+ <Flex>
83
+ <Icon as={CheckCircle} marginLeft={2} color="success600" />
84
+ </Flex>
85
+ </Tooltip>
86
+ )}
87
+ {attributes.madeByStrapi && (
88
+ <Tooltip description={madeByStrapiMessage}>
89
+ <Flex>
90
+ <Box
91
+ as="img"
92
+ src={madeByStrapiIcon}
93
+ alt={madeByStrapiMessage}
94
+ marginLeft={1}
95
+ width={6}
96
+ height="auto"
97
+ />
98
+ </Flex>
99
+ </Tooltip>
100
+ )}
101
+ </Flex>
102
+ </Typography>
103
+ </Box>
104
+ <Box paddingTop={2}>
105
+ <EllipsisText as="p" variant="omega" textColor="neutral600">
106
+ {attributes.description}
107
+ </EllipsisText>
108
+ </Box>
109
+ </Box>
110
+
111
+ <Stack horizontal size={2} style={{ alignSelf: 'flex-end' }} paddingTop={3}>
112
+ <LinkButton
113
+ size="S"
114
+ href={`https://market.strapi.io/plugins/${attributes.slug}`}
115
+ endIcon={<ExternalLink />}
116
+ aria-label={formatMessage(
117
+ {
118
+ id: 'admin.pages.MarketPlacePage.plugin.info.label',
119
+ defaultMessage: 'Learn more about {pluginName}',
120
+ },
121
+ { pluginName: attributes.name }
122
+ )}
123
+ variant="tertiary"
124
+ onClick={() => trackUsage('didPluginLearnMore')}
125
+ >
126
+ {formatMessage({
127
+ id: 'admin.pages.MarketPlacePage.plugin.info.text',
128
+ defaultMessage: 'Learn more',
129
+ })}
130
+ </LinkButton>
131
+ {isInstalled ? (
132
+ <Box paddingLeft={4}>
133
+ <Icon as={Check} marginRight={2} width={12} height={12} color="success600" />
134
+ <Typography variant="omega" textColor="success600" fontWeight="bold">
135
+ {formatMessage({
136
+ id: 'admin.pages.MarketPlacePage.plugin.installed',
137
+ defaultMessage: 'Installed',
138
+ })}
139
+ </Typography>
140
+ </Box>
141
+ ) : (
142
+ <CopyToClipboard
143
+ onCopy={() => {
144
+ trackUsage('willInstallPlugin');
145
+ toggleNotification({
146
+ type: 'success',
147
+ message: { id: 'admin.pages.MarketPlacePage.plugin.copy.success' },
148
+ });
149
+ }}
150
+ text={commandToCopy}
151
+ >
152
+ <Button size="S" startIcon={<Duplicate />} variant="secondary">
153
+ {formatMessage({
154
+ id: 'admin.pages.MarketPlacePage.plugin.copy',
155
+ defaultMessage: 'Copy install command',
156
+ })}
157
+ </Button>
158
+ </CopyToClipboard>
159
+ )}
160
+ </Stack>
161
+ </Flex>
162
+ );
163
+ };
164
+
165
+ PluginCard.propTypes = {
166
+ plugin: PropTypes.shape({
167
+ id: PropTypes.string.isRequired,
168
+ attributes: PropTypes.shape({
169
+ name: PropTypes.string.isRequired,
170
+ description: PropTypes.string.isRequired,
171
+ slug: PropTypes.string.isRequired,
172
+ npmPackageName: PropTypes.string.isRequired,
173
+ npmPackageUrl: PropTypes.string.isRequired,
174
+ repositoryUrl: PropTypes.string.isRequired,
175
+ logo: PropTypes.object.isRequired,
176
+ developerName: PropTypes.string.isRequired,
177
+ validated: PropTypes.bool.isRequired,
178
+ madeByStrapi: PropTypes.bool.isRequired,
179
+ strapiCompatibility: PropTypes.oneOf(['v3', 'v4']).isRequired,
180
+ }).isRequired,
181
+ }).isRequired,
182
+ installedPluginNames: PropTypes.arrayOf(PropTypes.string).isRequired,
183
+ useYarn: PropTypes.bool.isRequired,
184
+ };
185
+
186
+ export default PluginCard;
@@ -1,118 +1,216 @@
1
- import React, { useEffect } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
- import styled from 'styled-components';
4
3
  import { Helmet } from 'react-helmet';
5
- import { pxToRem, CheckPagePermissions, useTracking } from '@strapi/helper-plugin';
6
- import { Layout, HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
7
- import { Flex } from '@strapi/design-system/Flex';
8
- import { Box } from '@strapi/design-system/Box';
9
- import { Stack } from '@strapi/design-system/Stack';
10
- import { LinkButton } from '@strapi/design-system/LinkButton';
4
+ import { useQuery } from 'react-query';
5
+ import matchSorter from 'match-sorter';
6
+ import {
7
+ AnErrorOccurred,
8
+ CheckPagePermissions,
9
+ useFocusWhenNavigate,
10
+ useTracking,
11
+ LoadingIndicatorPage,
12
+ useNotification,
13
+ } from '@strapi/helper-plugin';
14
+ import { Grid, GridItem } from '@strapi/design-system/Grid';
15
+ import { Layout, HeaderLayout, ContentLayout, ActionLayout } from '@strapi/design-system/Layout';
11
16
  import { Main } from '@strapi/design-system/Main';
12
- import { Typography } from '@strapi/design-system/Typography';
13
- import ExternalLink from '@strapi/icons/ExternalLink';
14
- import adminPermissions from '../../permissions';
15
- import MarketplacePicture from './assets/marketplace-coming-soon.png';
16
-
17
- const CenterTypography = styled(Typography)`
18
- text-align: center;
19
- `;
17
+ import { Searchbar } from '@strapi/design-system/Searchbar';
18
+ import { LinkButton } from '@strapi/design-system/LinkButton';
19
+ import { useNotifyAT } from '@strapi/design-system/LiveRegions';
20
+ import Plus from '@strapi/icons/Plus';
20
21
 
21
- const Img = styled.img`
22
- width: ${190 / 16}rem;
23
- `;
22
+ import PluginCard from './components/PluginCard';
23
+ import { EmptyPluginSearch } from './components/EmptyPluginSearch';
24
+ import { fetchAppInformation } from './utils/api';
25
+ import useFetchInstalledPlugins from '../../hooks/useFetchInstalledPlugins';
26
+ import useFetchMarketplacePlugins from '../../hooks/useFetchMarketplacePlugins';
27
+ import adminPermissions from '../../permissions';
24
28
 
25
- const StackCentered = styled(Stack)`
26
- align-items: center;
27
- `;
29
+ const matchSearch = (plugins, search) => {
30
+ return matchSorter(plugins, search, {
31
+ keys: [
32
+ {
33
+ threshold: matchSorter.rankings.WORD_STARTS_WITH,
34
+ key: 'attributes.name',
35
+ },
36
+ { threshold: matchSorter.rankings.WORD_STARTS_WITH, key: 'attributes.description' },
37
+ ],
38
+ });
39
+ };
28
40
 
29
41
  const MarketPlacePage = () => {
30
42
  const { formatMessage } = useIntl();
31
43
  const { trackUsage } = useTracking();
44
+ const { notifyStatus } = useNotifyAT();
45
+ const trackUsageRef = useRef(trackUsage);
46
+ const toggleNotification = useNotification();
47
+ const [searchQuery, setSearchQuery] = useState('');
48
+
49
+ useFocusWhenNavigate();
50
+
51
+ const marketplaceTitle = formatMessage({
52
+ id: 'admin.pages.MarketPlacePage.title',
53
+ defaultMessage: 'Marketplace',
54
+ });
55
+
56
+ const notifyMarketplaceLoad = () => {
57
+ notifyStatus(
58
+ formatMessage(
59
+ {
60
+ id: 'app.utils.notify.data-loaded',
61
+ defaultMessage: 'The {target} has loaded',
62
+ },
63
+ { target: marketplaceTitle }
64
+ )
65
+ );
66
+ };
67
+
68
+ const {
69
+ status: marketplacePluginsStatus,
70
+ data: marketplacePluginsResponse,
71
+ } = useFetchMarketplacePlugins(notifyMarketplaceLoad);
72
+
73
+ const {
74
+ status: installedPluginsStatus,
75
+ data: installedPluginsResponse,
76
+ } = useFetchInstalledPlugins();
77
+
78
+ const { data: appInfoResponse, status: appInfoStatus } = useQuery(
79
+ 'app-information',
80
+ fetchAppInformation,
81
+ {
82
+ onError: () => {
83
+ toggleNotification({
84
+ type: 'warning',
85
+ message: { id: 'notification.error', defaultMessage: 'An error occured' },
86
+ });
87
+ },
88
+ }
89
+ );
90
+
91
+ const isLoading = [marketplacePluginsStatus, installedPluginsStatus, appInfoStatus].includes(
92
+ 'loading'
93
+ );
94
+
95
+ const hasFailed = [marketplacePluginsStatus, installedPluginsStatus, appInfoStatus].includes(
96
+ 'error'
97
+ );
32
98
 
33
99
  useEffect(() => {
34
- trackUsage('didGoToMarketplace');
35
- }, [trackUsage]);
100
+ trackUsageRef.current('didGoToMarketplace');
101
+ }, []);
36
102
 
37
- return (
38
- <CheckPagePermissions permissions={adminPermissions.marketplace.main}>
103
+ if (hasFailed) {
104
+ return (
39
105
  <Layout>
40
- <Main>
41
- <Helmet
42
- title={formatMessage({
43
- id: 'admin.pages.MarketPlacePage.helmet',
44
- defaultMessage: 'Marketplace - Plugins',
45
- })}
46
- />
47
- <HeaderLayout
48
- title={formatMessage({
49
- id: 'admin.pages.MarketPlacePage.title',
50
- defaultMessage: 'Marketplace',
51
- })}
52
- subtitle={formatMessage({
53
- id: 'admin.pages.MarketPlacePage.subtitle',
54
- defaultMessage: 'Get more out of Strapi',
55
- })}
56
- />
57
- <ContentLayout>
58
- <StackCentered
59
- size={0}
60
- hasRadius
61
- background="neutral0"
62
- shadow="tableShadow"
63
- paddingTop={10}
64
- paddingBottom={10}
65
- >
66
- <Box paddingBottom={7}>
67
- <Img
68
- alt={formatMessage({
69
- id: 'admin.pages.MarketPlacePage.illustration',
70
- defaultMessage: 'marketplace illustration',
71
- })}
72
- src={MarketplacePicture}
73
- />
74
- </Box>
75
- <Typography variant="alpha">
76
- {formatMessage({
77
- id: 'admin.pages.MarketPlacePage.coming-soon.1',
78
- defaultMessage: 'A new way to make Strapi awesome.',
79
- })}
80
- </Typography>
81
- <Typography variant="alpha" textColor="primary700">
82
- {formatMessage({
83
- id: 'admin.pages.MarketPlacePage.published',
84
- defaultMessage: 'Finally here.',
85
- })}
86
- </Typography>
87
- <Flex maxWidth={pxToRem(620)} paddingTop={3}>
88
- <CenterTypography variant="epsilon" textColor="neutral600">
89
- {formatMessage({
90
- id: 'admin.pages.MarketPlacePage.content.subtitle.published',
91
- defaultMessage:
92
- 'The web marketplace helps you get the most of Strapi. In addition, we are working hard to offer the best experience to discover and install plugins, directly from the app.',
93
- })}
94
- </CenterTypography>
95
- </Flex>
96
- <Stack paddingTop={6} horizontal size={2}>
97
- <LinkButton href="https://market.strapi.io" size="L" variant="primary" endIcon={<ExternalLink />}>
98
- {formatMessage({
99
- id: 'admin.pages.MarketPlacePage.submit.market.link',
100
- defaultMessage: 'Visit the web marketplace',
101
- })}
102
- </LinkButton>
103
- <LinkButton href="https://market.strapi.io/submit-plugin" size="L" variant="secondary">
104
- {formatMessage({
105
- id: 'admin.pages.MarketPlacePage.submit.plugin.link',
106
- defaultMessage: 'Submit your plugin',
107
- })}
108
- </LinkButton>
109
- </Stack>
110
- </StackCentered>
111
- </ContentLayout>
106
+ <AnErrorOccurred />
107
+ </Layout>
108
+ );
109
+ }
110
+
111
+ if (isLoading) {
112
+ return (
113
+ <Layout>
114
+ <Main aria-busy>
115
+ <LoadingIndicatorPage />
112
116
  </Main>
113
117
  </Layout>
114
- </CheckPagePermissions>
118
+ );
119
+ }
120
+
121
+ const searchResults = matchSearch(marketplacePluginsResponse.data, searchQuery);
122
+ const installedPluginNames = installedPluginsResponse.plugins.map(plugin => plugin.packageName);
123
+
124
+ return (
125
+ <Layout>
126
+ <Main>
127
+ <Helmet
128
+ title={formatMessage({
129
+ id: 'admin.pages.MarketPlacePage.helmet',
130
+ defaultMessage: 'Marketplace - Plugins',
131
+ })}
132
+ />
133
+ <HeaderLayout
134
+ title={formatMessage({
135
+ id: 'admin.pages.MarketPlacePage.title',
136
+ defaultMessage: 'Marketplace',
137
+ })}
138
+ subtitle={formatMessage({
139
+ id: 'admin.pages.MarketPlacePage.subtitle',
140
+ defaultMessage: 'Get more out of Strapi',
141
+ })}
142
+ primaryAction={
143
+ <LinkButton
144
+ startIcon={<Plus />}
145
+ variant="tertiary"
146
+ href="https://market.strapi.io/submit-plugin"
147
+ onClick={() => trackUsage('didSubmitPlugin')}
148
+ >
149
+ {formatMessage({
150
+ id: 'admin.pages.MarketPlacePage.submit.plugin.link',
151
+ defaultMessage: 'Submit your plugin',
152
+ })}
153
+ </LinkButton>
154
+ }
155
+ />
156
+ <ActionLayout
157
+ startActions={
158
+ <Searchbar
159
+ name="searchbar"
160
+ onClear={() => setSearchQuery('')}
161
+ value={searchQuery}
162
+ onChange={e => setSearchQuery(e.target.value)}
163
+ clearLabel={formatMessage({
164
+ id: 'admin.pages.MarketPlacePage.search.clear',
165
+ defaultMessage: 'Clear the plugin search',
166
+ })}
167
+ placeholder={formatMessage({
168
+ id: 'admin.pages.MarketPlacePage.search.placeholder',
169
+ defaultMessage: 'Search for a plugin',
170
+ })}
171
+ >
172
+ {formatMessage({
173
+ id: 'admin.pages.MarketPlacePage.search.placeholder',
174
+ defaultMessage: 'Search for a plugin',
175
+ })}
176
+ </Searchbar>
177
+ }
178
+ />
179
+ <ContentLayout>
180
+ {searchQuery.length > 0 && !searchResults.length ? (
181
+ <EmptyPluginSearch
182
+ content={formatMessage(
183
+ {
184
+ id: 'admin.pages.MarketPlacePage.search.empty',
185
+ defaultMessage: 'No result for "{target}"',
186
+ },
187
+ { target: searchQuery }
188
+ )}
189
+ />
190
+ ) : (
191
+ <Grid gap={4}>
192
+ {searchResults.map(plugin => (
193
+ <GridItem col={4} s={6} xs={12} style={{ height: '100%' }} key={plugin.id}>
194
+ <PluginCard
195
+ plugin={plugin}
196
+ installedPluginNames={installedPluginNames}
197
+ useYarn={appInfoResponse.data.useYarn}
198
+ />
199
+ </GridItem>
200
+ ))}
201
+ </Grid>
202
+ )}
203
+ </ContentLayout>
204
+ </Main>
205
+ </Layout>
115
206
  );
116
207
  };
117
208
 
118
- export default MarketPlacePage;
209
+ const ProtectedMarketPlace = () => (
210
+ <CheckPagePermissions permissions={adminPermissions.marketplace.main}>
211
+ <MarketPlacePage />
212
+ </CheckPagePermissions>
213
+ );
214
+
215
+ export { MarketPlacePage };
216
+ export default ProtectedMarketPlace;
@@ -0,0 +1,9 @@
1
+ import { axiosInstance } from '../../../core/utils';
2
+
3
+ const fetchAppInformation = async () => {
4
+ const { data } = await axiosInstance.get('/admin/information');
5
+
6
+ return data;
7
+ };
8
+
9
+ export { fetchAppInformation };
@@ -182,7 +182,7 @@ const ProfilePage = () => {
182
182
  />
183
183
  <Box paddingBottom={10}>
184
184
  <ContentLayout>
185
- <Stack size={6}>
185
+ <Stack spacing={6}>
186
186
  <Box
187
187
  background="neutral0"
188
188
  hasRadius
@@ -192,7 +192,7 @@ const ProfilePage = () => {
192
192
  paddingLeft={7}
193
193
  paddingRight={7}
194
194
  >
195
- <Stack size={4}>
195
+ <Stack spacing={4}>
196
196
  <Typography variant="delta" as="h2">
197
197
  {formatMessage({
198
198
  id: 'Settings.profile.form.section.profile.title',
@@ -263,7 +263,7 @@ const ProfilePage = () => {
263
263
  paddingLeft={7}
264
264
  paddingRight={7}
265
265
  >
266
- <Stack size={4}>
266
+ <Stack spacing={4}>
267
267
  <Typography variant="delta" as="h2">
268
268
  {formatMessage({
269
269
  id: 'Settings.profile.form.section.password.title',
@@ -410,8 +410,8 @@ const ProfilePage = () => {
410
410
  paddingLeft={7}
411
411
  paddingRight={7}
412
412
  >
413
- <Stack size={4}>
414
- <Stack size={1}>
413
+ <Stack spacing={4}>
414
+ <Stack spacing={1}>
415
415
  <Typography variant="delta" as="h2">
416
416
  {formatMessage({
417
417
  id: 'Settings.profile.form.section.experience.title',
@@ -29,7 +29,10 @@ const SettingsNav = ({ menu }) => {
29
29
  };
30
30
  });
31
31
 
32
- const label = formatMessage({ id: 'app.components.LeftMenuLinkContainer.settings' });
32
+ const label = formatMessage({
33
+ id: 'app.components.LeftMenuLinkContainer.settings',
34
+ defaultMessage: 'Settings',
35
+ });
33
36
 
34
37
  return (
35
38
  <SubNav ariaLabel={label}>