@strapi/admin 4.2.0-alpha.O → 4.2.0-beta.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.
- package/admin/src/app.js +7 -4
- package/admin/src/assets/images/icon_made-by-strapi.svg +5 -0
- package/admin/src/components/AuthenticatedApp/index.js +9 -2
- package/admin/src/components/AuthenticatedApp/utils/api.js +20 -1
- package/admin/src/components/GuidedTour/Homepage/index.js +13 -3
- package/admin/src/components/GuidedTour/Modal/index.js +4 -1
- package/admin/src/components/GuidedTour/layout.js +9 -0
- package/admin/src/components/PrivateRoute/index.js +23 -17
- package/admin/src/content-manager/components/DynamicZone/components/ComponentPicker/Category/index.js +4 -1
- package/admin/src/content-manager/components/Inputs/index.js +3 -4
- package/admin/src/content-manager/components/SelectWrapper/utils/getSelectStyles.js +2 -1
- package/admin/src/content-manager/pages/EditView/index.js +0 -188
- package/admin/src/hooks/index.js +0 -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/pages/Admin/index.js +1 -3
- package/admin/src/pages/AuthPage/index.js +28 -5
- 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/translations/en.json +18 -15
- package/admin/src/translations/ja.json +2 -2
- package/admin/src/translations/ko.json +2 -2
- package/admin/src/translations/vi.json +30 -4
- package/build/4362.b3d67035.chunk.js +1 -0
- package/build/90f49a385afb000fb1d4.svg +5 -0
- package/build/9260.4233fae2.chunk.js +2 -0
- package/build/{9260.fa40c7bd.chunk.js.LICENSE.txt → 9260.4233fae2.chunk.js.LICENSE.txt} +0 -0
- package/build/Admin-authenticatedApp.16bed71f.chunk.js +1 -0
- package/build/Admin_homePage.fd1fc572.chunk.js +1 -0
- package/build/Admin_marketplace.89a0a014.chunk.js +1 -0
- package/build/Admin_pluginsPage.97a514db.chunk.js +1 -0
- package/build/admin-users.2740c223.chunk.js +1 -0
- package/build/content-manager.f1c46a88.chunk.js +1 -0
- package/build/en-json.73a610d6.chunk.js +1 -0
- package/build/index.html +1 -1
- package/build/ja-json.e13f04e8.chunk.js +1 -0
- package/build/ko-json.2200c9c9.chunk.js +1 -0
- package/build/main.12d62562.js +2 -0
- package/build/{main.3e719c82.js.LICENSE.txt → main.12d62562.js.LICENSE.txt} +0 -0
- package/build/{runtime~main.66becdbd.js → runtime~main.21bf3a67.js} +1 -1
- package/build/{upload-translation-en-json.c3373c8d.chunk.js → upload-translation-en-json.c334dd82.chunk.js} +1 -1
- package/build/vi-json.1e850069.chunk.js +1 -0
- package/index.js +53 -244
- package/package.json +9 -7
- package/server/controllers/admin.js +12 -1
- package/server/routes/serve-admin-panel.js +1 -1
- package/utils/create-cache-dir.js +119 -0
- package/utils/get-custom-webpack-config.js +38 -0
- package/utils/index.js +13 -0
- package/utils/should-build-admin.js +51 -0
- package/utils/watch-admin-files.js +56 -0
- package/webpack.config.js +36 -3
- 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/build/01a600d9e6e0dea21e33.png +0 -0
- package/build/4362.86a4e939.chunk.js +0 -1
- package/build/9260.fa40c7bd.chunk.js +0 -2
- package/build/Admin-authenticatedApp.a5d2c5fa.chunk.js +0 -1
- package/build/Admin_homePage.d6754c66.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-users.1fda1f27.chunk.js +0 -1
- package/build/content-manager.8412e024.chunk.js +0 -1
- package/build/en-json.b35c285f.chunk.js +0 -1
- package/build/ja-json.46e29f04.chunk.js +0 -1
- package/build/ko-json.dd36fdc0.chunk.js +0 -1
- package/build/main.3e719c82.js +0 -2
- package/build/vi-json.55a11ac0.chunk.js +0 -1
|
@@ -14,7 +14,10 @@ import init from './init';
|
|
|
14
14
|
import { initialState, reducer } from './reducer';
|
|
15
15
|
|
|
16
16
|
const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
push,
|
|
19
|
+
location: { search },
|
|
20
|
+
} = useHistory();
|
|
18
21
|
const { changeLocale } = useLocalesProvider();
|
|
19
22
|
const { setSkipped } = useGuidedTour();
|
|
20
23
|
const { trackUsage } = useTracking();
|
|
@@ -119,7 +122,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
119
122
|
auth.setToken(token, body.rememberMe);
|
|
120
123
|
auth.setUserInfo(user, body.rememberMe);
|
|
121
124
|
|
|
122
|
-
|
|
125
|
+
redirectToPreviousLocation();
|
|
123
126
|
} catch (err) {
|
|
124
127
|
if (err.response) {
|
|
125
128
|
const errorMessage = get(
|
|
@@ -174,6 +177,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
174
177
|
if (isUserSuperAdmin) {
|
|
175
178
|
persistStateToLocaleStorage.setSkipped(false);
|
|
176
179
|
setSkipped(false);
|
|
180
|
+
trackUsage('didLaunchGuidedtour');
|
|
177
181
|
}
|
|
178
182
|
}
|
|
179
183
|
|
|
@@ -189,8 +193,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
189
193
|
return;
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
|
|
193
|
-
push('/');
|
|
196
|
+
redirectToPreviousLocation();
|
|
194
197
|
} catch (err) {
|
|
195
198
|
trackUsage('didNotCreateFirstAdmin');
|
|
196
199
|
|
|
@@ -238,6 +241,17 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
238
241
|
}
|
|
239
242
|
};
|
|
240
243
|
|
|
244
|
+
const redirectToPreviousLocation = () => {
|
|
245
|
+
if (authType === 'login') {
|
|
246
|
+
const redirectTo = query.get('redirectTo');
|
|
247
|
+
const redirectUrl = redirectTo ? decodeURIComponent(redirectTo) : '/';
|
|
248
|
+
|
|
249
|
+
push(redirectUrl);
|
|
250
|
+
} else {
|
|
251
|
+
push('/');
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
241
255
|
// Redirect the user to the login page if
|
|
242
256
|
// the endpoint does not exist or
|
|
243
257
|
// there is already an admin user oo
|
|
@@ -248,7 +262,16 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
|
|
248
262
|
|
|
249
263
|
// Redirect the user to the register-admin if it is the first user
|
|
250
264
|
if (!hasAdmin && authType !== 'register-admin') {
|
|
251
|
-
return
|
|
265
|
+
return (
|
|
266
|
+
<Redirect
|
|
267
|
+
to={{
|
|
268
|
+
pathname: '/auth/register-admin',
|
|
269
|
+
// Forward the `?redirectTo` from /auth/login
|
|
270
|
+
// /abc => /auth/login?redirectTo=%2Fabc => /auth/register-admin?redirectTo=%2Fabc
|
|
271
|
+
search,
|
|
272
|
+
}}
|
|
273
|
+
/>
|
|
274
|
+
);
|
|
252
275
|
}
|
|
253
276
|
|
|
254
277
|
return (
|
|
@@ -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;
|