Merge pull request #12556 from strapi/market-list-plugins

List plugins on in-app marketplace
This commit is contained in:
Rémi de Juvigny 2022-02-21 12:15:44 +01:00 committed by GitHub
commit a7fec64fc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 7775 additions and 1356 deletions

View File

@ -1,7 +1,6 @@
export { default as useConfigurations } from './useConfigurations';
export { default as useModels } from './useModels';
export { default as useFetchPermissionsLayout } from './useFetchPermissionsLayout';
export { default as useFetchPluginsFromMarketPlace } from './useFetchPluginsFromMarketPlace';
export { default as useFetchRole } from './useFetchRole';
export { default as useMenu } from './useMenu';
export { default as useRolesList } from './useRolesList';

View File

@ -1,49 +0,0 @@
import { useEffect, useState } from 'react';
import axios from 'axios';
import { useIntl } from 'react-intl';
const useFetchPluginsFromMarketPlace = () => {
const { locale: currentLocale } = useIntl();
const [state, setState] = useState({
error: false,
isLoading: true,
data: null,
});
useEffect(() => {
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
const getData = async () => {
try {
const { data } = await axios.get('https://marketplace.strapi.io/plugins', {
cancelToken: source.token,
params: { lang: currentLocale },
});
setState({
isLoading: false,
data,
error: false,
});
} catch (err) {
if (axios.isCancel(err)) {
// Silent
} else {
// handle error
setState(prev => ({ ...prev, isLoading: false, error: true }));
}
}
};
getData();
return () => {
source.cancel();
};
}, [currentLocale]);
return state;
};
export default useFetchPluginsFromMarketPlace;

View File

@ -1,46 +0,0 @@
import { renderHook } from '@testing-library/react-hooks';
import axios from 'axios';
// eslint-disable-next-line import/no-unresolved
import MockAdapter from 'axios-mock-adapter';
import useFetch from '../index';
jest.mock('react-intl', () => ({ useIntl: () => ({ locale: 'en' }) }));
describe('ADMIN | hooks | useFetchPluginsFromMarketPlace', () => {
it('should perform a GET request', async () => {
const mock = new MockAdapter(axios);
const mockData = [{ ok: true }];
mock.onGet().replyOnce(200, mockData);
const { result, waitForNextUpdate } = renderHook(() => useFetch());
expect(result.current.isLoading).toBeTruthy();
await waitForNextUpdate();
expect(result.current.isLoading).toBeFalsy();
expect(result.current.error).toBeFalsy();
expect(result.current.data).toEqual(mockData);
});
it('should handle the errors correctly', async () => {
const mock = new MockAdapter(axios);
mock.onGet().replyOnce(() => {
return new Promise((_, reject) => {
reject(new Error(''));
});
});
const { result, waitForNextUpdate } = renderHook(() => useFetch());
expect(result.current.isLoading).toBeTruthy();
await waitForNextUpdate();
expect(result.current.isLoading).toBeFalsy();
expect(result.current.error).toBeTruthy();
expect(result.current.data).toBeNull();
});
});

View File

@ -32,7 +32,7 @@ const Plugins = () => {
);
};
const { status, data } = useQuery('list-plugins', () => fetchPlugins(notifyLoad), {
const { status, data } = useQuery('list-installed-plugins', () => fetchPlugins(notifyLoad), {
onError: () => {
toggleNotification({
type: 'warning',

View File

@ -1,28 +0,0 @@
import styled from 'styled-components';
// TODO : To migrate with @strapi/design-system
const Wrapper = styled.div`
display: flex;
align-items: center;
margin-top: 2rem;
border-radius: 4px;
border-left: 4px solid ${({ theme }) => theme.main.colors.mediumBlue};
box-shadow: 0 2px 4px ${({ theme }) => theme.main.colors.darkGrey};
padding: 8px 16px;
.bannerImage {
width: 48px;
height: 48px;
margin-right: 16px;
}
.bannerLink {
color: ${({ theme }) => theme.main.colors.mediumBlue};
font-weight: ${({ theme }) => theme.main.fontWeights.bold};
svg {
margin-left: 4px;
}
}
`;
export default Wrapper;

View File

@ -1,37 +0,0 @@
import React from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
import { useTracking } from '@strapi/helper-plugin';
import Wrapper from './Wrapper';
import LogoStrapi from '../../../assets/images/banner_strapi-rocket.png';
const MarketplaceBanner = () => {
const { formatMessage } = useIntl();
const { trackUsage } = useTracking();
return (
<Wrapper>
<img
className="bannerImage"
src={LogoStrapi}
alt={formatMessage({ id: 'app.components.MarketplaceBanner.image.alt' })}
/>
<div>
<div>
<FormattedMessage id="app.components.MarketplaceBanner" />
</div>
<a
href="https://github.com/strapi/awesome-strapi"
target="_blank"
rel="noopener noreferrer"
className="bannerLink"
onClick={() => trackUsage('didGoToStrapiAwesome')}
>
<FormattedMessage id="app.components.MarketplaceBanner.link" />
<i className="fa fa-external-link-alt" />
</a>
</div>
</Wrapper>
);
};
export default MarketplaceBanner;

View File

@ -1,148 +0,0 @@
import styled from 'styled-components';
const Wrapper = styled.div`
.wrapper {
position: relative;
min-height: 216px;
margin-bottom: 3.6rem;
padding: 1.2rem 1.5rem;
padding-bottom: 0;
background-color: #fff;
box-shadow: 0 2px 4px #e3e9f3;
-webkit-font-smoothing: antialiased;
}
.cardTitle {
display: flex;
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
> div:first-child {
margin-right: 14px;
}
> div:last-child {
height: 36px;
line-height: 36px;
}
i,
svg {
margin-left: 7px;
color: #b3b5b9;
font-size: 1rem;
vertical-align: baseline;
cursor: pointer;
}
}
.cardDescription {
height: 54px;
margin-top: 27px;
margin-bottom: 9px;
font-size: 13px;
font-weight: 400;
> span:last-child {
color: #1c5de7;
}
-webkit-font-smoothing: antialiased;
}
.cardFooter {
position: absolute;
bottom: 0;
left: 0;
display: flex;
width: 100%;
height: 45px;
padding: 0.9rem 1.5rem 1rem;
background-color: #fafafb;
justify-content: space-between;
flex-direction: row-reverse;
cursor: initial;
}
.compatible {
margin-top: 3px;
color: #5a9e06;
font-size: 1.3rem;
font-style: italic;
> i,
> svg {
margin-right: 7px;
font-size: 12px;
}
}
.settings {
margin-top: 3px;
color: #323740;
font-size: 1.3rem;
font-weight: 500;
cursor: pointer;
> i,
> svg {
margin-right: 7px;
font-size: 11px;
vertical-align: inherit;
}
}
.button {
height: 26px;
min-width: 89px !important;
padding: 0 15px;
margin: 0;
border-radius: 2px !important;
line-height: 23px;
font-size: 13px;
cursor: pointer;
span {
display: inline-block;
width: 100%;
height: 100%;
}
}
.frame {
width: 70px;
height: 36px;
margin: auto 0;
text-align: center;
border: 1px solid #f3f3f7;
border-radius: 3px;
white-space: nowrap;
> img {
max-height: 36px;
vertical-align: middle;
}
}
.helper {
display: inline-block;
height: 100%;
vertical-align: middle;
}
.primary {
background: linear-gradient(315deg, #0097f6 0%, #005eea 100%);
color: white;
font-weight: 500;
-webkit-font-smoothing: antialiased;
&:active {
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.15);
}
}
.secondary {
border: 1px solid #dfe0e1;
font-weight: 600;
}
`;
export default Wrapper;

View File

@ -1,185 +0,0 @@
/**
*
* PluginCard
*
*/
/* eslint-disable */
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { Button, PopUpWarning, CheckPermissions } from '@strapi/helper-plugin';
import adminPermissions from '../../../permissions';
import Wrapper from './Wrapper';
/* eslint-disable react/no-unused-state */
class PluginCard extends React.Component {
state = {
boostrapCol: 'col-lg-4',
showModalAutoReload: false,
showModalEnv: false,
};
componentDidMount() {
// Listen window resize.
window.addEventListener('resize', this.setBoostrapCol);
this.setBoostrapCol();
}
componentWillUnmount() {
window.removeEventListener('resize', this.setBoostrapCol);
}
setBoostrapCol = () => {
let boostrapCol = 'col-lg-4';
if (window.innerWidth > 1680) {
boostrapCol = 'col-lg-3';
}
if (window.innerWidth > 2300) {
boostrapCol = 'col-lg-2';
}
this.setState({ boostrapCol });
};
handleDownloadPlugin = e => {
const {
autoReload,
currentEnvironment,
downloadPlugin,
history: { push },
isAlreadyInstalled,
plugin: { id },
} = this.props;
if (!autoReload) {
this.setState({ showModalAutoReload: true });
} else if (currentEnvironment !== 'development') {
this.setState({ showModalEnv: true });
} else if (!isAlreadyInstalled) {
downloadPlugin(id);
} else {
push('/list-plugins');
}
};
render() {
const buttonClass = !this.props.isAlreadyInstalled ? 'primary' : 'secondary';
const buttonLabel = this.props.isAlreadyInstalled
? 'app.components.PluginCard.Button.label.install'
: 'app.components.PluginCard.Button.label.download';
const settingsComponent = null;
const descriptions = {
short:
this.props.plugin.id === 'support-us' ? (
<FormattedMessage id={this.props.plugin.description.short} />
) : (
this.props.plugin.description.short
),
long:
this.props.plugin.id === 'support-us' ? (
<FormattedMessage
id={this.props.plugin.description.long || this.props.plugin.description.short}
/>
) : (
this.props.plugin.description.long || this.props.plugin.description.short
),
};
return (
<Wrapper className={this.state.boostrapCol}>
<div className="wrapper">
<div className="cardTitle">
<div className="frame">
<span className="helper" />
<img src={this.props.plugin.logo} alt="icon" />
</div>
<div
onClick={e => {
// FIXME: dead link as we are changing the naming. Would be better to use a url comming from the api call directly
window.open(
`https://github.com/strapi/strapi/tree/master/packages/strapi-plugin-${this.props.plugin.id}`,
'_blank'
);
}}
>
{this.props.plugin.name} <i className="fa fa-external-link-alt" />
</div>
</div>
<div className="cardDescription">{descriptions.long}</div>
<div className="cardFooter" onClick={e => e.stopPropagation()}>
<div className="cardFooterButton">
<CheckPermissions permissions={adminPermissions.marketplace.install}>
<Button
className={`${buttonClass} button`}
label={buttonLabel}
type="button"
onClick={this.handleDownloadPlugin}
/>
</CheckPermissions>
</div>
{this.props.isAlreadyInstalled ? (
settingsComponent
) : (
<div className="compatible">
<i className={`fa fa-${this.props.plugin.isCompatible ? 'check' : 'times'}`} />
<FormattedMessage
id={`app.components.PluginCard.compatible${
this.props.plugin.id === 'support-us' ? 'Community' : ''
}`}
/>
</div>
)}
</div>
</div>
<PopUpWarning
content={{
message: 'app.components.PluginCard.PopUpWarning.install.impossible.autoReload.needed',
title: 'app.components.PluginCard.PopUpWarning.install.impossible.title',
confirm: 'app.components.PluginCard.PopUpWarning.install.impossible.confirm',
}}
isOpen={this.state.showModalAutoReload}
onlyConfirmButton
onConfirm={() => this.setState({ showModalAutoReload: false })}
popUpWarningType="warning"
/>
<PopUpWarning
content={{
message: 'app.components.PluginCard.PopUpWarning.install.impossible.environment',
title: 'app.components.PluginCard.PopUpWarning.install.impossible.title',
confirm: 'app.components.PluginCard.PopUpWarning.install.impossible.confirm',
}}
isOpen={this.state.showModalEnv}
onlyConfirmButton
onConfirm={() => this.setState({ showModalEnv: false })}
popUpWarningType="warning"
/>
</Wrapper>
);
}
}
PluginCard.defaultProps = {
isAlreadyInstalled: false,
plugin: {
description: '',
id: '',
name: '',
price: 0,
ratings: 5,
},
};
PluginCard.propTypes = {
currentEnvironment: PropTypes.string.isRequired,
downloadPlugin: PropTypes.func.isRequired,
history: PropTypes.object.isRequired,
isAlreadyInstalled: PropTypes.bool,
plugin: PropTypes.object,
};
export default PluginCard;

View File

@ -1,5 +0,0 @@
import styled from 'styled-components';
const Wrapper = styled.div``;
export default Wrapper;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

View File

@ -0,0 +1,111 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
import styled from 'styled-components';
import { Box } from '@strapi/design-system/Box';
import { Stack } from '@strapi/design-system/Stack';
import { Typography } from '@strapi/design-system/Typography';
import { Button } from '@strapi/design-system/Button';
import { LinkButton } from '@strapi/design-system/LinkButton';
import { Flex } from '@strapi/design-system/Flex';
import ExternalLink from '@strapi/icons/ExternalLink';
import Duplicate from '@strapi/icons/Duplicate';
// Custom component to have an ellipsis after the 2nd line
const EllipsisText = styled(Typography)`
/* stylelint-disable value-no-vendor-prefix, property-no-vendor-prefix */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
/* stylelint-enable value-no-vendor-prefix, property-no-vendor-prefix */
overflow: hidden;
`;
const PluginCard = ({ plugin }) => {
const { attributes } = plugin;
const { formatMessage } = useIntl();
return (
<Flex
direction="column"
justifyContent="space-between"
paddingTop={4}
paddingRight={6}
paddingBottom={4}
paddingLeft={6}
hasRadius
background="neutral0"
shadow="tableShadow"
height="100%"
alignItems="normal"
>
<Box>
<Box
as="img"
src={attributes.logo.url}
alt={`${attributes.name} logo`}
hasRadius
width={11}
height={11}
/>
<Box paddingTop={5}>
<Typography as="h3" variant="delta">
{attributes.name}
</Typography>
</Box>
<Box paddingTop={2}>
<EllipsisText as="p" variant="omega" textColor="neutral600">
{attributes.description}
</EllipsisText>
</Box>
</Box>
<Stack horizontal size={2} style={{ alignSelf: 'flex-end' }} paddingTop={3}>
<LinkButton
size="S"
href={`https://market.strapi.io/plugins/${attributes.slug}`}
endIcon={<ExternalLink />}
aria-label={formatMessage(
{
id: 'admin.pages.MarketPlacePage.plugin.info.label',
defaultMessage: 'Learn more about {pluginName}',
},
{ pluginName: attributes.name }
)}
variant="tertiary"
>
{formatMessage({
id: 'admin.pages.MarketPlacePage.plugin.info.text',
defaultMessage: 'Learn more',
})}
</LinkButton>
<Button size="S" endIcon={<Duplicate />} variant="secondary">
{formatMessage({
id: 'admin.pages.MarketPlacePage.plugin.copy',
defaultMessage: 'Copy install command',
})}
</Button>
</Stack>
</Flex>
);
};
PluginCard.propTypes = {
plugin: PropTypes.shape({
id: PropTypes.string.isRequired,
attributes: PropTypes.shape({
name: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
npmPackageName: PropTypes.string.isRequired,
npmPackageUrl: PropTypes.string.isRequired,
repositoryUrl: PropTypes.string.isRequired,
logo: PropTypes.object.isRequired,
developerName: PropTypes.string.isRequired,
validated: PropTypes.bool.isRequired,
strapiCompatibility: PropTypes.oneOf(['v3', 'v4']).isRequired,
}).isRequired,
}).isRequired,
};
export default PluginCard;

View File

@ -1,38 +1,21 @@
import React, { useEffect } from 'react';
import { useIntl } from 'react-intl';
import { useQuery } from 'react-query';
import styled from 'styled-components';
import { Helmet } from 'react-helmet';
import {
pxToRem,
CheckPagePermissions,
useFocusWhenNavigate,
useTracking,
LoadingIndicatorPage,
useNotification,
} from '@strapi/helper-plugin';
import { useNotifyAT } from '@strapi/design-system/LiveRegions';
import { Grid, GridItem } from '@strapi/design-system/Grid';
import { Layout, HeaderLayout, ContentLayout } from '@strapi/design-system/Layout';
import { Flex } from '@strapi/design-system/Flex';
import { Box } from '@strapi/design-system/Box';
import { Stack } from '@strapi/design-system/Stack';
import { LinkButton } from '@strapi/design-system/LinkButton';
import { Main } from '@strapi/design-system/Main';
import { Typography } from '@strapi/design-system/Typography';
import { fetchPlugins } from './utils/api';
import adminPermissions from '../../permissions';
import MarketplacePicture from './assets/marketplace-coming-soon.png';
const CenterTypography = styled(Typography)`
text-align: center;
`;
const Img = styled.img`
width: ${190 / 16}rem;
`;
const StackCentered = styled(Stack)`
align-items: center;
`;
import PluginCard from './components/PluginCard';
const MarketPlacePage = () => {
const { formatMessage } = useIntl();
@ -40,6 +23,8 @@ const MarketPlacePage = () => {
const toggleNotification = useNotification();
const { notifyStatus } = useNotifyAT();
useFocusWhenNavigate();
const title = formatMessage({
id: 'admin.pages.MarketPlacePage.title',
defaultMessage: 'Marketplace',
@ -57,14 +42,18 @@ const MarketPlacePage = () => {
);
};
const { status, data } = useQuery('list-plugins', () => fetchPlugins(notifyLoad), {
const { status, data: pluginsResponse } = useQuery(
'list-marketplace-plugins',
() => fetchPlugins(notifyLoad),
{
onError: () => {
toggleNotification({
type: 'warning',
message: { id: 'notification.error', defaultMessage: 'An error occured' },
});
},
});
}
);
const isLoading = status !== 'success' && status !== 'error';
@ -82,11 +71,7 @@ const MarketPlacePage = () => {
);
}
// TODO: Remove when using data, logging for now to avoid lint error
console.log('plugins', data);
return (
<CheckPagePermissions permissions={adminPermissions.marketplace.main}>
<Layout>
<Main>
<Helmet
@ -106,65 +91,24 @@ const MarketPlacePage = () => {
})}
/>
<ContentLayout>
<StackCentered
size={0}
hasRadius
background="neutral0"
shadow="tableShadow"
paddingTop={10}
paddingBottom={10}
>
<Box paddingBottom={7}>
<Img
alt={formatMessage({
id: 'admin.pages.MarketPlacePage.illustration',
defaultMessage: 'marketplace illustration',
})}
src={MarketplacePicture}
/>
</Box>
<Typography variant="alpha">
{formatMessage({
id: 'admin.pages.MarketPlacePage.coming-soon.1',
defaultMessage: 'A new way to make Strapi awesome.',
})}
</Typography>
<Typography variant="alpha" textColor="primary700">
{formatMessage({
id: 'admin.pages.MarketPlacePage.coming-soon.2',
defaultMessage: 'A new way to make Strapi awesome.',
})}
</Typography>
<Flex maxWidth={pxToRem(580)} paddingTop={3}>
<CenterTypography variant="epsilon" textColor="neutral600">
{formatMessage({
id: 'admin.pages.MarketPlacePage.content.subtitle',
defaultMessage:
'The new marketplace will help you get more out of Strapi. We are working hard to offer the best experience to discover and install plugins.',
})}
</CenterTypography>
</Flex>
<Stack paddingTop={6} horizontal size={2}>
{/* Temporarily hidden until we have the right URL for the link */}
{/* <LinkButton href="https://strapi.io/" size="L" variant="secondary">
{formatMessage({
id: 'admin.pages.MarketPlacePage.submit.plugin.link',
defaultMessage: 'Submit your plugin',
})}
</LinkButton> */}
<LinkButton href="https://strapi.io/blog/strapi-market-is-coming-soon" size="L">
{formatMessage({
id: 'admin.pages.MarketPlacePage.blog.link',
defaultMessage: 'Read our blog post',
})}
</LinkButton>
</Stack>
</StackCentered>
<Grid gap={4}>
{pluginsResponse.data.map(plugin => (
<GridItem col={4} s={6} xs={12} style={{ height: '100%' }} key={plugin.id}>
<PluginCard plugin={plugin} />
</GridItem>
))}
</Grid>
</ContentLayout>
</Main>
</Layout>
</CheckPagePermissions>
);
};
export default MarketPlacePage;
const ProtectedMarketPlace = () => (
<CheckPagePermissions permissions={adminPermissions.marketplace.main}>
<MarketPlacePage />
</CheckPagePermissions>
);
export { MarketPlacePage };
export default ProtectedMarketPlace;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,550 @@
import { setupServer } from 'msw/node';
import { rest } from 'msw';
const handlers = [
rest.get('*/plugins', (req, res, ctx) => {
return res(
ctx.delay(100),
ctx.status(200),
ctx.json({
data: [
{
id: 'recQHmaveQknjaIdv',
attributes: {
name: 'Cloudflare pages',
description:
'This plugin lets you easily trigger Cloudflare Pages builds from Strapi.',
slug: 'strapi-plugin-cloudflare-pages',
npmPackageName: 'strapi-plugin-cloudflare-pages',
npmPackageUrl: 'https://www.npmjs.com/package/strapi-plugin-cloudflare-pages',
repositoryUrl: 'https://github.com/sarhugo/strapi-plugin-cloudflare-pages',
logo: {
id: 'attzLRKpax5MIrMtq',
width: 160,
height: 160,
url:
'https://dl.airtable.com/.attachments/3e586bbcdd7dc2effc3770fd49506aa6/ddbb2540/cf-logo-v-rgb.jpg',
filename: 'cf-logo-v-rgb.jpg',
size: 25615,
type: 'image/jpeg',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/aa98b52525fdeb5767cf554563a9df14/7a03d156',
width: 36,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/a0e91d3564a0f41d89799e352b06c8ee/3d45329e',
width: 160,
height: 160,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/20b4afecf2bf8bc081c54eae92a7f599/d1e10f39',
width: 3000,
height: 3000,
},
},
},
developerName: 'Hugo Sarti',
validated: false,
strapiCompatibility: 'v3',
},
},
{
id: 'recWQXzTM5e6Friiq',
attributes: {
name: 'Comments',
description: 'Powerful Strapi based comments moderation tool for you and your users',
slug: 'strapi-plugin-comments',
npmPackageName: 'strapi-plugin-comments',
npmPackageUrl: 'https://www.npmjs.com/package/strapi-plugin-comments',
repositoryUrl: 'https://github.com/VirtusLab-Open-Source/strapi-plugin-comments',
logo: {
id: 'att1xGwmQzDOC2UwY',
width: 1080,
height: 1080,
url:
'https://dl.airtable.com/.attachments/eb4cd59876565af77c9c3e5966b59f10/2111bfc8/vl_strapi-comments.png',
filename: 'vl_strapi-comments.png',
size: 344804,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/92ec34110ff65c0993eac95a4f9ee906/76443a36',
width: 36,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/5136e180187206b2a90bfa9a5ca65149/64187ef0',
width: 512,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/4c3fdf040d05779adf251a737adcae55/590ff600',
width: 3000,
height: 3000,
},
},
},
screenshots: [
{
id: 'att4y0AbotGdAOdEJ',
width: 1920,
height: 1080,
url:
'https://dl.airtable.com/.attachments/f6316615095b33ce67700a3810c1869c/a13c0ef1/screens.png',
filename: 'screens.png',
size: 625578,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/2ff8cdfc0f4c215ae63e96f21281b93d/d817fa87',
width: 64,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/4ecf8b86b1af9061bcdf90f42253531b/1a88263e',
width: 910,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/1f51a4dca550912863c9d43a8052ab77/e4fa5ec5',
width: 3000,
height: 3000,
},
},
},
{
id: 'att3k9vSnVo0KFSEA',
width: 2880,
height: 1578,
url:
'https://dl.airtable.com/.attachments/a3c3451815e017baa04f4b92f26c29ef/243d8656/Screenshot2022-01-27at07.48.19.png',
filename: 'Screenshot 2022-01-27 at 07.48.19.png',
size: 1545826,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/51d91e1e39eacf40f05fa193d367a306/1dbd8409',
width: 66,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/392a6d72b82000b0b84d58c40319d3a0/2ccc6e5b',
width: 934,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/45fc5b9d4bc83d2ef5e0aa36d2918b8d/a05010ef',
width: 3000,
height: 3000,
},
},
},
{
id: 'attKuQmvrbDnp1tm1',
width: 2880,
height: 1582,
url:
'https://dl.airtable.com/.attachments/d75abd544b3710060288547049d7bf22/1412b396/Screenshot2022-01-27at07.48.37.png',
filename: 'Screenshot 2022-01-27 at 07.48.37.png',
size: 1282068,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/c41517eac949e0da38f0f19344fe875b/cec423d6',
width: 66,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/e7881ee4b0b75fc0f2d40c53efa94b01/6513dcc2',
width: 932,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/60d7b535cc8b579ee09a64ce23ec607e/954df7b1',
width: 3000,
height: 3000,
},
},
},
{
id: 'att3iUd2fPh6xgsU4',
width: 2880,
height: 1580,
url:
'https://dl.airtable.com/.attachments/87efb22ce60c67971a16bac929bb24aa/ef2da9b7/Screenshot2022-01-27at07.48.47.png',
filename: 'Screenshot 2022-01-27 at 07.48.47.png',
size: 1386662,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/8e7ddb79f9ff320235a029df98872d74/85632ae0',
width: 66,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/6d6ad9667c04b08709dc3e6f2f68f064/da46238e',
width: 933,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/ebd28cadcab85bd217b066eeeb113723/6267c7e1',
width: 3000,
height: 3000,
},
},
},
{
id: 'attSRD1UvyPislYg4',
width: 2880,
height: 1580,
url:
'https://dl.airtable.com/.attachments/3b104067762dedac336a938f57676f93/4deb3bae/Screenshot2022-01-27at07.48.07.png',
filename: 'Screenshot 2022-01-27 at 07.48.07.png',
size: 1282540,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/c215d62e79d7d13d2c40016f3118a979/37e7af8e',
width: 66,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/4895150e0da9ba7edfa32cf2348d79cb/f5e8af82',
width: 933,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/8e7a00bd492421447458cd2ee739aba2/bc5f3759',
width: 3000,
height: 3000,
},
},
},
],
developerName: 'Mateusz Ziarko',
validated: false,
strapiCompatibility: 'v4',
},
},
{
id: 'rec0KouDUCBhydNW6',
attributes: {
name: 'Config Sync',
description:
'Migrate your config data across environments using the CLI or Strapi admin panel.',
slug: 'strapi-plugin-config-sync',
npmPackageName: 'strapi-plugin-config-sync',
npmPackageUrl: 'https://www.npmjs.com/package/strapi-plugin-config-sync',
repositoryUrl: 'https://github.com/boazpoolman/strapi-plugin-config-sync',
logo: {
id: 'att6OefK4471IpCZ5',
width: 320,
height: 320,
url:
'https://dl.airtable.com/.attachments/e23a7231d12cce89cb4b05cbfe759d45/96f5f496/Screenshot2021-12-09at22.15.37.png',
filename: 'Screenshot 2021-12-09 at 22.15.37.png',
size: 11580,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/2f69cd2d5a884ad733c2e18bbe3d2e39/9cf40c6f',
width: 36,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/8301d585b4aaa2977fa1e007feb02a17/1d121e13',
width: 320,
height: 320,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/15d442f81a47ff6ff002fd25ce6788d6/395c8aea',
width: 3000,
height: 3000,
},
},
},
screenshots: [
{
id: 'att0QiAtNRfoz3mwF',
width: 2206,
height: 1284,
url:
'https://dl.airtable.com/.attachments/b6afdee7abfbf5c63ef3d2de243f99a4/a5f3f48c/config-diff.png',
filename: 'config-diff.png',
size: 332901,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/2135d552f535ead55971c9ae016bb178/5b71c76e',
width: 62,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/4b972ba187e2bd589c10987871ef00df/35f22c62',
width: 880,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/3b88bba22b7d9d9ec456bf849efeac53/423f1a93',
width: 3000,
height: 3000,
},
},
},
],
developerName: 'Boaz Poolman',
validated: true,
strapiCompatibility: 'v4',
},
},
{
id: 'rec0Z7KLBCtaD6rC3',
attributes: {
name: 'Content Versioning',
description:
'This plugin enables you to versioning Content Types. It allows multiple draft versions✅ Keeps history of all changes (with time travel) ✅ ',
slug: '@notum-cz-strapi-plugin-content-versioning',
npmPackageName: '@notum-cz/strapi-plugin-content-versioning',
npmPackageUrl:
'https://www.npmjs.com/package/@notum-cz/strapi-plugin-content-versioning',
repositoryUrl: 'https://github.com/notum-cz/strapi-plugin-content-versioning',
logo: {
id: 'attaMdJdER0feFBuX',
width: 1280,
height: 1280,
url:
'https://dl.airtable.com/.attachments/0b86f18e2606ed7f53bd54d536a1bea5/13a87f30/Artboard7copy.png',
filename: 'Artboard 7 copy.png',
size: 35705,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/4c0179127f923c0ce01866c92e9664a8/347fbe74',
width: 36,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/0edca5708e30dc9fce6c962dcc28dbce/39b83021',
width: 512,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/a44ea395865d5f6ba71dbcae814cfc83/31c3fffc',
width: 3000,
height: 3000,
},
},
},
screenshots: [
{
id: 'attGD52ggRIgnsUKp',
width: 1920,
height: 1080,
url:
'https://dl.airtable.com/.attachments/50126ddff8ef4ed7b7a693ecd156ae60/b6a9a4a6/Novprojekt15.png',
filename: 'Nový projekt (15).png',
size: 52163,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/60382a6542a77f90594526b68fe5182c/f54e7675',
width: 64,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/05deee4e93b9b57e9cd4465e2c6622f1/1b377c4a',
width: 910,
height: 512,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/87ef842cda9b0647e7dd4d2f8a086379/303ecd98',
width: 3000,
height: 3000,
},
},
},
],
developerName: 'Ondřej Janošík',
validated: false,
strapiCompatibility: 'v4',
},
},
{
id: 'recwrVXGrUoOHdSlO',
attributes: {
name: 'Documentation',
description: 'Create an OpenAPI Document and visualize your API with SWAGGER UI',
slug: '@strapi-plugin-documentation',
npmPackageName: '@strapi/plugin-documentation',
npmPackageUrl: 'https://www.npmjs.com/package/@strapi/plugin-documentation',
repositoryUrl:
'https://github.com/strapi/strapi/tree/master/packages/plugins/documentation',
logo: {
id: 'att22dETRlzkfVWAl',
width: 225,
height: 225,
url:
'https://dl.airtable.com/.attachments/b6998ac52e8b0460b8a14ced8074b788/2a4d4a90/swagger.png',
filename: 'swagger.png',
size: 6052,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/71a6b03e03a6b26647991a617478cdfa/8e1d8d2b',
width: 36,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/c249c4953d5bb0e2f58ed7174afecc2f/796dca09',
width: 225,
height: 225,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/4b7bc3a765f3927b9fcd12809ddf82a8/d3a4b97b',
width: 3000,
height: 3000,
},
},
},
developerName: 'Strapi team',
validated: true,
strapiCompatibility: 'v4',
},
},
{
id: 'recqR0rWAw5gZHc1c',
attributes: {
name: 'Documentation v3',
description: 'Create an OpenAPI Document and visualize your API with SWAGGER UI',
slug: 'strapi-plugin-documentation',
npmPackageName: 'strapi-plugin-documentation',
npmPackageUrl: 'https://www.npmjs.com/package/strapi-plugin-documentation',
repositoryUrl:
'https://github.com/strapi/strapi/tree/v3.6.9/packages/strapi-plugin-documentation',
logo: {
id: 'att22dETRlzkfVWAl',
width: 225,
height: 225,
url:
'https://dl.airtable.com/.attachments/b6998ac52e8b0460b8a14ced8074b788/2a4d4a90/swagger.png',
filename: 'swagger.png',
size: 6052,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/71a6b03e03a6b26647991a617478cdfa/8e1d8d2b',
width: 36,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/c249c4953d5bb0e2f58ed7174afecc2f/796dca09',
width: 225,
height: 225,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/4b7bc3a765f3927b9fcd12809ddf82a8/d3a4b97b',
width: 3000,
height: 3000,
},
},
},
developerName: 'Strapi team',
validated: true,
strapiCompatibility: 'v3',
},
},
{
id: 'rec5s49X99wA67Ubj',
attributes: {
name: 'Transformer',
description:
'A plugin for Strapi Headless CMS that provides the ability to transform the API response. ',
slug: 'strapi-plugin-transformer',
npmPackageName: 'strapi-plugin-transformer',
npmPackageUrl: 'https://www.npmjs.com/package/strapi-plugin-transformer',
repositoryUrl: 'https://github.com/ComfortablyCoding/strapi-plugin-transformer',
logo: {
id: 'attbggDs1BgpGByTz',
width: 158,
height: 158,
url:
'https://dl.airtable.com/.attachments/5ffd1782a2fabf423ccd6f56c562f31a/b8f8598f/transformer-logo.png',
filename: 'transformer-logo.png',
size: 5787,
type: 'image/png',
thumbnails: {
small: {
url:
'https://dl.airtable.com/.attachmentThumbnails/53ee41b18d2f704257a483ea17de9020/07cabde5',
width: 36,
height: 36,
},
large: {
url:
'https://dl.airtable.com/.attachmentThumbnails/46185b0bddbd1684ac02d12d490a621b/37a04f6c',
width: 158,
height: 158,
},
full: {
url:
'https://dl.airtable.com/.attachmentThumbnails/1a6e9c656aacaca5536abbe2c2964d30/5e904c9c',
width: 3000,
height: 3000,
},
},
},
developerName: 'Daedalus',
validated: false,
strapiCompatibility: 'v4',
},
},
],
})
);
}),
];
const server = setupServer(...handlers);
export default server;

View File

@ -1,11 +1,19 @@
import axios from 'axios';
const MARKETPLACE_API_URL = 'https://market-api.strapi.io';
const fetchPlugins = async notify => {
const { data } = await axios.get(`${process.env.STRAPI_ADMIN_MARKETPLACE_API_URL}/plugins`);
const { data: response } = await axios.get(`${MARKETPLACE_API_URL}/plugins`);
// Only keep v4 plugins
const filteredResponse = {
...response,
data: response.data.filter(plugin => plugin.attributes.strapiCompatibility === 'v4'),
};
notify();
return data;
return filteredResponse;
};
export { fetchPlugins };

View File

@ -59,14 +59,12 @@
"Auth.privacy-policy-agreement.terms": "terms",
"Auth.reset-password.title": "Reset password",
"admin.pages.MarketPlacePage.helmet": "Marketplace - Plugins",
"admin.pages.MarketPlacePage.illustration": "marketplace illustration",
"admin.pages.MarketPlacePage.title": "Marketplace",
"admin.pages.MarketPlacePage.subtitle": "Get more out of Strapi",
"admin.pages.MarketPlacePage.coming-soon.1": "A new way to make Strapi awesome.",
"admin.pages.MarketPlacePage.coming-soon.2": "Coming soon.",
"admin.pages.MarketPlacePage.content.subtitle": "The new marketplace will help you get more out of Strapi. We are working hard to offer the best experience to discover and install plugins.",
"admin.pages.MarketPlacePage.plugin.info.text": "Learn more",
"admin.pages.MarketPlacePage.plugin.info.label": "Learn more about {pluginName}",
"admin.pages.MarketPlacePage.plugin.copy": "Copy install command",
"admin.pages.MarketPlacePage.submit.plugin.link": "Submit your plugin",
"admin.pages.MarketPlacePage.blog.link": "Read our blog post",
"Content Manager": "Content Manager",
"Content Type Builder": "Content-Types Builder",
"Documentation": "Documentation",

View File

@ -25,8 +25,6 @@ const getClientEnvironment = options => {
ADMIN_PATH: options.adminPath,
NODE_ENV: options.env || 'development',
STRAPI_ADMIN_BACKEND_URL: options.backend,
STRAPI_ADMIN_MARKETPLACE_API_URL:
process.env.STRAPI_ADMIN_MARKETPLACE_API_URL || 'https://market-api.strapi.io',
}
);

6702
yarn.lock

File diff suppressed because it is too large Load Diff