@strapi/admin 4.2.0-alpha.5 → 4.2.0-alpha.8
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.
- package/admin/src/StrapiApp.js +40 -42
- package/admin/src/assets/images/icon_made-by-strapi.svg +5 -0
- package/admin/src/components/GuidedTour/Modal/components/Modal.js +1 -1
- package/admin/src/components/PrivateRoute/index.js +23 -17
- package/admin/src/components/Providers/index.js +65 -32
- package/admin/src/components/Theme/index.js +11 -12
- package/admin/src/components/ThemeToggleProvider/index.js +66 -0
- package/admin/src/content-manager/components/SelectWrapper/utils/getSelectStyles.js +3 -1
- package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +4 -2
- package/admin/src/contexts/ThemeToggle/index.js +5 -0
- package/admin/src/contexts/index.js +1 -0
- package/admin/src/hooks/index.js +1 -1
- package/admin/src/hooks/useFetchInstalledPlugins/index.js +23 -0
- package/admin/src/{pages/InstalledPluginsPage → hooks/useFetchInstalledPlugins}/utils/api.js +2 -4
- package/admin/src/hooks/useFetchMarketplacePlugins/index.js +23 -0
- package/admin/src/hooks/useFetchMarketplacePlugins/utils/api.js +17 -0
- package/admin/src/hooks/useThemeToggle/index.js +10 -0
- package/admin/src/pages/Admin/Onboarding/index.js +1 -1
- package/admin/src/pages/AuthPage/index.js +22 -5
- package/admin/src/pages/HomePage/SocialLinks.js +0 -3
- package/admin/src/pages/InstalledPluginsPage/Plugins.js +6 -15
- package/admin/src/pages/MarketplacePage/components/EmptyPluginSearch/EmptyPluginGrid.js +27 -0
- package/admin/src/pages/MarketplacePage/components/EmptyPluginSearch/index.js +30 -0
- package/admin/src/pages/MarketplacePage/components/PluginCard/index.js +186 -0
- package/admin/src/pages/MarketplacePage/index.js +199 -107
- package/admin/src/pages/MarketplacePage/utils/api.js +9 -0
- package/admin/src/pages/ProfilePage/index.js +74 -10
- package/admin/src/pages/ProfilePage/utils/api.js +4 -2
- package/admin/src/translations/en.json +21 -15
- package/build/4362.91a4253f.chunk.js +1 -0
- package/build/{6250.836851ca.chunk.js → 6404.3c2d0a81.chunk.js} +1 -1
- package/build/90f49a385afb000fb1d4.svg +5 -0
- package/build/{Admin-authenticatedApp.a24ebaa0.chunk.js → Admin-authenticatedApp.187bd266.chunk.js} +1 -1
- package/build/{Admin_homePage.86604515.chunk.js → Admin_homePage.fd1fc572.chunk.js} +1 -1
- package/build/Admin_marketplace.89a0a014.chunk.js +1 -0
- package/build/Admin_pluginsPage.97a514db.chunk.js +1 -0
- package/build/Admin_profilePage.d7192d06.chunk.js +1 -0
- package/build/{Admin_settingsPage.55ec1f30.chunk.js → Admin_settingsPage.a8c7ded5.chunk.js} +1 -1
- package/build/admin-users.2740c223.chunk.js +1 -0
- package/build/content-manager.f1c46a88.chunk.js +1 -0
- package/build/{content-type-builder.de5d18ad.chunk.js → content-type-builder.cda4ba3c.chunk.js} +1 -1
- package/build/{email-settings-page.27ee4a98.chunk.js → email-settings-page.40ee2bda.chunk.js} +1 -1
- package/build/en-json.73a610d6.chunk.js +1 -0
- package/build/index.html +1 -1
- package/build/{main.4ea77c5f.js → main.c12537d5.js} +2 -2
- package/build/{main.4ea77c5f.js.LICENSE.txt → main.c12537d5.js.LICENSE.txt} +0 -0
- package/build/{runtime~main.83b9ee0b.js → runtime~main.e5240b25.js} +1 -1
- package/build/users-email-settings-page.5abb9575.chunk.js +1 -0
- package/package.json +5 -5
- package/server/controllers/admin.js +12 -1
- package/.env +0 -0
- package/admin/src/hooks/useFetchPluginsFromMarketPlace/index.js +0 -49
- package/admin/src/pages/MarketplacePage/MarketplaceBanner/Wrapper.js +0 -28
- package/admin/src/pages/MarketplacePage/MarketplaceBanner/index.js +0 -37
- package/admin/src/pages/MarketplacePage/PluginCard/Wrapper.js +0 -148
- package/admin/src/pages/MarketplacePage/PluginCard/index.js +0 -185
- package/admin/src/pages/MarketplacePage/Wrapper.js +0 -5
- package/admin/src/pages/MarketplacePage/assets/marketplace-coming-soon.png +0 -0
- package/admin/src/themes/colors.js +0 -51
- package/admin/src/themes/fontWeights.js +0 -8
- package/admin/src/themes/index.js +0 -13
- package/admin/src/themes/sizes.js +0 -31
- package/build/01a600d9e6e0dea21e33.png +0 -0
- package/build/4362.dbe98749.chunk.js +0 -1
- package/build/Admin_marketplace.419010d8.chunk.js +0 -1
- package/build/Admin_pluginsPage.7d1bd7ce.chunk.js +0 -1
- package/build/Admin_profilePage.c497b39d.chunk.js +0 -1
- package/build/admin-users.1fda1f27.chunk.js +0 -1
- package/build/content-manager.31be1448.chunk.js +0 -1
- package/build/en-json.bce44d39.chunk.js +0 -1
- package/build/users-email-settings-page.862eb51e.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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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 } =
|
|
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={4}>
|
|
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 spacing={2} style={{ alignSelf: 'flex-end' }} paddingTop={6}>
|
|
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,127 +1,219 @@
|
|
|
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 {
|
|
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';
|
|
6
15
|
import { Layout, HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
|
|
7
|
-
import {
|
|
16
|
+
import { Main } from '@strapi/design-system/Main';
|
|
17
|
+
import { Searchbar } from '@strapi/design-system/Searchbar';
|
|
8
18
|
import { Box } from '@strapi/design-system/Box';
|
|
9
|
-
import { Stack } from '@strapi/design-system/Stack';
|
|
10
19
|
import { LinkButton } from '@strapi/design-system/LinkButton';
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
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
|
-
`;
|
|
20
|
+
import { useNotifyAT } from '@strapi/design-system/LiveRegions';
|
|
21
|
+
import Upload from '@strapi/icons/Upload';
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
import PluginCard from './components/PluginCard';
|
|
24
|
+
import { EmptyPluginSearch } from './components/EmptyPluginSearch';
|
|
25
|
+
import { fetchAppInformation } from './utils/api';
|
|
26
|
+
import useFetchInstalledPlugins from '../../hooks/useFetchInstalledPlugins';
|
|
27
|
+
import useFetchMarketplacePlugins from '../../hooks/useFetchMarketplacePlugins';
|
|
28
|
+
import adminPermissions from '../../permissions';
|
|
24
29
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
const matchSearch = (plugins, search) => {
|
|
31
|
+
return matchSorter(plugins, search, {
|
|
32
|
+
keys: [
|
|
33
|
+
{
|
|
34
|
+
threshold: matchSorter.rankings.WORD_STARTS_WITH,
|
|
35
|
+
key: 'attributes.name',
|
|
36
|
+
},
|
|
37
|
+
{ threshold: matchSorter.rankings.WORD_STARTS_WITH, key: 'attributes.description' },
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
};
|
|
28
41
|
|
|
29
42
|
const MarketPlacePage = () => {
|
|
30
43
|
const { formatMessage } = useIntl();
|
|
31
44
|
const { trackUsage } = useTracking();
|
|
45
|
+
const { notifyStatus } = useNotifyAT();
|
|
46
|
+
const trackUsageRef = useRef(trackUsage);
|
|
47
|
+
const toggleNotification = useNotification();
|
|
48
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
49
|
+
|
|
50
|
+
useFocusWhenNavigate();
|
|
51
|
+
|
|
52
|
+
const marketplaceTitle = formatMessage({
|
|
53
|
+
id: 'admin.pages.MarketPlacePage.title',
|
|
54
|
+
defaultMessage: 'Marketplace',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const notifyMarketplaceLoad = () => {
|
|
58
|
+
notifyStatus(
|
|
59
|
+
formatMessage(
|
|
60
|
+
{
|
|
61
|
+
id: 'app.utils.notify.data-loaded',
|
|
62
|
+
defaultMessage: 'The {target} has loaded',
|
|
63
|
+
},
|
|
64
|
+
{ target: marketplaceTitle }
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const {
|
|
70
|
+
status: marketplacePluginsStatus,
|
|
71
|
+
data: marketplacePluginsResponse,
|
|
72
|
+
} = useFetchMarketplacePlugins(notifyMarketplaceLoad);
|
|
73
|
+
|
|
74
|
+
const {
|
|
75
|
+
status: installedPluginsStatus,
|
|
76
|
+
data: installedPluginsResponse,
|
|
77
|
+
} = useFetchInstalledPlugins();
|
|
78
|
+
|
|
79
|
+
const { data: appInfoResponse, status: appInfoStatus } = useQuery(
|
|
80
|
+
'app-information',
|
|
81
|
+
fetchAppInformation,
|
|
82
|
+
{
|
|
83
|
+
onError: () => {
|
|
84
|
+
toggleNotification({
|
|
85
|
+
type: 'warning',
|
|
86
|
+
message: { id: 'notification.error', defaultMessage: 'An error occured' },
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const isLoading = [marketplacePluginsStatus, installedPluginsStatus, appInfoStatus].includes(
|
|
93
|
+
'loading'
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const hasFailed = [marketplacePluginsStatus, installedPluginsStatus, appInfoStatus].includes(
|
|
97
|
+
'error'
|
|
98
|
+
);
|
|
32
99
|
|
|
33
100
|
useEffect(() => {
|
|
34
|
-
|
|
35
|
-
}, [
|
|
101
|
+
trackUsageRef.current('didGoToMarketplace');
|
|
102
|
+
}, []);
|
|
36
103
|
|
|
37
|
-
|
|
38
|
-
|
|
104
|
+
if (hasFailed) {
|
|
105
|
+
return (
|
|
39
106
|
<Layout>
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
defaultMessage: 'Get more out of Strapi',
|
|
55
|
-
})}
|
|
56
|
-
/>
|
|
57
|
-
<ContentLayout>
|
|
58
|
-
<StackCentered
|
|
59
|
-
spacing={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 spacing={2}>
|
|
97
|
-
<LinkButton
|
|
98
|
-
href="https://market.strapi.io"
|
|
99
|
-
size="L"
|
|
100
|
-
variant="primary"
|
|
101
|
-
endIcon={<ExternalLink />}
|
|
102
|
-
>
|
|
103
|
-
{formatMessage({
|
|
104
|
-
id: 'admin.pages.MarketPlacePage.submit.market.link',
|
|
105
|
-
defaultMessage: 'Visit the web marketplace',
|
|
106
|
-
})}
|
|
107
|
-
</LinkButton>
|
|
108
|
-
<LinkButton
|
|
109
|
-
href="https://market.strapi.io/submit-plugin"
|
|
110
|
-
size="L"
|
|
111
|
-
variant="secondary"
|
|
112
|
-
>
|
|
113
|
-
{formatMessage({
|
|
114
|
-
id: 'admin.pages.MarketPlacePage.submit.plugin.link',
|
|
115
|
-
defaultMessage: 'Submit your plugin',
|
|
116
|
-
})}
|
|
117
|
-
</LinkButton>
|
|
118
|
-
</Stack>
|
|
119
|
-
</StackCentered>
|
|
120
|
-
</ContentLayout>
|
|
107
|
+
<ContentLayout>
|
|
108
|
+
<Box paddingTop={8}>
|
|
109
|
+
<AnErrorOccurred />
|
|
110
|
+
</Box>
|
|
111
|
+
</ContentLayout>
|
|
112
|
+
</Layout>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (isLoading) {
|
|
117
|
+
return (
|
|
118
|
+
<Layout>
|
|
119
|
+
<Main aria-busy>
|
|
120
|
+
<LoadingIndicatorPage />
|
|
121
121
|
</Main>
|
|
122
122
|
</Layout>
|
|
123
|
-
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const searchResults = matchSearch(marketplacePluginsResponse.data, searchQuery);
|
|
127
|
+
const installedPluginNames = installedPluginsResponse.plugins.map(plugin => plugin.packageName);
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<Layout>
|
|
131
|
+
<Main>
|
|
132
|
+
<Helmet
|
|
133
|
+
title={formatMessage({
|
|
134
|
+
id: 'admin.pages.MarketPlacePage.helmet',
|
|
135
|
+
defaultMessage: 'Marketplace - Plugins',
|
|
136
|
+
})}
|
|
137
|
+
/>
|
|
138
|
+
<HeaderLayout
|
|
139
|
+
title={formatMessage({
|
|
140
|
+
id: 'admin.pages.MarketPlacePage.title',
|
|
141
|
+
defaultMessage: 'Marketplace',
|
|
142
|
+
})}
|
|
143
|
+
subtitle={formatMessage({
|
|
144
|
+
id: 'admin.pages.MarketPlacePage.subtitle',
|
|
145
|
+
defaultMessage: 'Get more out of Strapi',
|
|
146
|
+
})}
|
|
147
|
+
primaryAction={
|
|
148
|
+
<LinkButton
|
|
149
|
+
startIcon={<Upload />}
|
|
150
|
+
variant="tertiary"
|
|
151
|
+
href="https://market.strapi.io/submit-plugin"
|
|
152
|
+
onClick={() => trackUsage('didSubmitPlugin')}
|
|
153
|
+
>
|
|
154
|
+
{formatMessage({
|
|
155
|
+
id: 'admin.pages.MarketPlacePage.submit.plugin.link',
|
|
156
|
+
defaultMessage: 'Submit your plugin',
|
|
157
|
+
})}
|
|
158
|
+
</LinkButton>
|
|
159
|
+
}
|
|
160
|
+
/>
|
|
161
|
+
<ContentLayout>
|
|
162
|
+
<Box width="25%" paddingBottom={4}>
|
|
163
|
+
<Searchbar
|
|
164
|
+
name="searchbar"
|
|
165
|
+
onClear={() => setSearchQuery('')}
|
|
166
|
+
value={searchQuery}
|
|
167
|
+
onChange={e => setSearchQuery(e.target.value)}
|
|
168
|
+
clearLabel={formatMessage({
|
|
169
|
+
id: 'admin.pages.MarketPlacePage.search.clear',
|
|
170
|
+
defaultMessage: 'Clear the plugin search',
|
|
171
|
+
})}
|
|
172
|
+
placeholder={formatMessage({
|
|
173
|
+
id: 'admin.pages.MarketPlacePage.search.placeholder',
|
|
174
|
+
defaultMessage: 'Search for a plugin',
|
|
175
|
+
})}
|
|
176
|
+
>
|
|
177
|
+
{formatMessage({
|
|
178
|
+
id: 'admin.pages.MarketPlacePage.search.placeholder',
|
|
179
|
+
defaultMessage: 'Search for a plugin',
|
|
180
|
+
})}
|
|
181
|
+
</Searchbar>
|
|
182
|
+
</Box>
|
|
183
|
+
{searchQuery.length > 0 && !searchResults.length ? (
|
|
184
|
+
<EmptyPluginSearch
|
|
185
|
+
content={formatMessage(
|
|
186
|
+
{
|
|
187
|
+
id: 'admin.pages.MarketPlacePage.search.empty',
|
|
188
|
+
defaultMessage: 'No result for "{target}"',
|
|
189
|
+
},
|
|
190
|
+
{ target: searchQuery }
|
|
191
|
+
)}
|
|
192
|
+
/>
|
|
193
|
+
) : (
|
|
194
|
+
<Grid gap={4}>
|
|
195
|
+
{searchResults.map(plugin => (
|
|
196
|
+
<GridItem col={4} s={6} xs={12} style={{ height: '100%' }} key={plugin.id}>
|
|
197
|
+
<PluginCard
|
|
198
|
+
plugin={plugin}
|
|
199
|
+
installedPluginNames={installedPluginNames}
|
|
200
|
+
useYarn={appInfoResponse.data.useYarn}
|
|
201
|
+
/>
|
|
202
|
+
</GridItem>
|
|
203
|
+
))}
|
|
204
|
+
</Grid>
|
|
205
|
+
)}
|
|
206
|
+
</ContentLayout>
|
|
207
|
+
</Main>
|
|
208
|
+
</Layout>
|
|
124
209
|
);
|
|
125
210
|
};
|
|
126
211
|
|
|
127
|
-
|
|
212
|
+
const ProtectedMarketPlace = () => (
|
|
213
|
+
<CheckPagePermissions permissions={adminPermissions.marketplace.main}>
|
|
214
|
+
<MarketPlacePage />
|
|
215
|
+
</CheckPagePermissions>
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
export { MarketPlacePage };
|
|
219
|
+
export default ProtectedMarketPlace;
|