mirror of
https://github.com/strapi/strapi.git
synced 2025-08-26 17:53:10 +00:00
Update homepage design
This commit is contained in:
parent
a8e0dcb245
commit
eef4939415
@ -87,7 +87,6 @@
|
||||
"type": "group",
|
||||
"group": "ingredients",
|
||||
"repeatable": true,
|
||||
"min": 1,
|
||||
"max": 10
|
||||
},
|
||||
"author": {
|
||||
|
@ -1,13 +1,14 @@
|
||||
.wrapper {
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 372px;
|
||||
height: 159px;
|
||||
border-radius: 2px;
|
||||
background-color: #fff;
|
||||
}
|
||||
`;
|
||||
|
||||
.content {
|
||||
const Content = styled.div`
|
||||
padding-top: 3rem;
|
||||
// color: #F64D0A;
|
||||
text-align: center;
|
||||
font-family: Lato;
|
||||
font-size: 1.3rem;
|
||||
@ -26,9 +27,10 @@
|
||||
}
|
||||
|
||||
> span {
|
||||
color: #787E8F;
|
||||
color: #787e8f;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
}
|
||||
export { Content, Wrapper };
|
@ -1,26 +1,27 @@
|
||||
/*
|
||||
*
|
||||
* DownloadInfo
|
||||
*
|
||||
*/
|
||||
*
|
||||
* DownloadInfo
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Content, Wrapper } from './components';
|
||||
import Icon from '../../assets/icons/icon_success.svg';
|
||||
import styles from './styles.scss';
|
||||
|
||||
function DownloadInfo() {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.content}>
|
||||
<Wrapper>
|
||||
<Content>
|
||||
<img src={Icon} alt="info" />
|
||||
<div>
|
||||
<FormattedMessage id="app.components.DownloadInfo.download" />
|
||||
<br />
|
||||
<FormattedMessage id="app.components.DownloadInfo.text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Content>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,8 +0,0 @@
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
|
||||
export default Loadable({
|
||||
loader: () => import('./index'),
|
||||
loading: LoadingIndicator,
|
||||
});
|
@ -1,36 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* HomePageBlock
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function HomePageBlock({ children, className }) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
className,
|
||||
styles.homePageBlock,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
HomePageBlock.defaultProps = {
|
||||
children: '',
|
||||
className: '',
|
||||
};
|
||||
|
||||
HomePageBlock.propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default HomePageBlock;
|
@ -1,8 +0,0 @@
|
||||
.homePageBlock {
|
||||
width: 100%;
|
||||
margin-bottom: 34px;
|
||||
background: #ffffff;
|
||||
padding: 20px 30px 30px 30px;
|
||||
box-shadow: 0 2px 4px 0 #E3E9F3;
|
||||
border-radius: 3px;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
|
||||
export default Loadable({
|
||||
loader: () => import('./index'),
|
||||
loading: LoadingIndicator,
|
||||
});
|
@ -1,76 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Sub
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { isFunction, isObject } from 'lodash';
|
||||
import cn from 'classnames';
|
||||
import { LoadingBar } from 'strapi-helper-plugin';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function Sub({ bordered, content, link, name, style, title, underline }) {
|
||||
if (isObject(title)) {
|
||||
return (
|
||||
<div className={cn(styles.subWrapper, bordered && styles.subBordered)}>
|
||||
<FormattedMessage {...title}>
|
||||
{message => (
|
||||
<span className={cn(underline && styles.underlinedTitle)}>
|
||||
{message}
|
||||
{name}
|
||||
</span>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
{content()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className={cn(
|
||||
styles.subWrapper,
|
||||
bordered && styles.subBordered,
|
||||
styles.link
|
||||
)}
|
||||
href={`https://blog.strapi.io/${link}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span>{title}</span>
|
||||
{title === '' && <LoadingBar />}
|
||||
{content === '' && <LoadingBar style={{ width: '40%' }} />}
|
||||
<p style={style}>{isFunction(content) ? content() : content}</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
Sub.defaultProps = {
|
||||
bordered: false,
|
||||
content: () => '',
|
||||
link: '',
|
||||
name: '',
|
||||
style: {},
|
||||
title: {
|
||||
id: 'app.utils.defaultMessage',
|
||||
defaultMessage: 'app.utils.defaultMessage',
|
||||
values: {},
|
||||
},
|
||||
underline: false,
|
||||
};
|
||||
|
||||
Sub.propTypes = {
|
||||
bordered: PropTypes.bool,
|
||||
content: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
||||
link: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
title: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
underline: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Sub;
|
@ -1,46 +0,0 @@
|
||||
.subBordered {
|
||||
margin-bottom: 18px;
|
||||
border-bottom: 1px solid #F7F8F8;
|
||||
}
|
||||
|
||||
.subWrapper {
|
||||
position: relative;
|
||||
line-height: 18px;
|
||||
text-decoration: none;
|
||||
|
||||
> span {
|
||||
text-decoration: none;
|
||||
font-family: Lato-Bold;
|
||||
font-size: 20px;
|
||||
color: #333740;
|
||||
letter-spacing: 0;
|
||||
transition: color .2s ease;
|
||||
}
|
||||
|
||||
p {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
max-width: calc(100% - 150px);
|
||||
margin-top: 18px;
|
||||
color: #333740;
|
||||
font-size: 14px;
|
||||
transition: color .2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.underlinedTitle {
|
||||
border-bottom: 3px solid #F0B41E;
|
||||
}
|
||||
|
||||
.link{
|
||||
&:hover, &:focus, &:active{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
> span, p {
|
||||
color: lighten(#333740, 20%);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import { LoadingIndicator } from 'strapi-helper-plugin';
|
||||
|
||||
export default Loadable({
|
||||
loader: () => import('./index'),
|
||||
loading: LoadingIndicator,
|
||||
});
|
@ -1,28 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* SupportUsCta
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function SupportUsCta() {
|
||||
return (
|
||||
<FormattedMessage id="app.components.HomePage.support.link">
|
||||
{message => (
|
||||
<a
|
||||
href="https://strapi.io/shop"
|
||||
target="_blank"
|
||||
className={styles.supportUsCta}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
);
|
||||
}
|
||||
|
||||
export default SupportUsCta;
|
@ -1,18 +0,0 @@
|
||||
.supportUsCta {
|
||||
padding: 7px 12px 7px 20px;
|
||||
border: 1px solid #FFFFFF;
|
||||
border-radius: 3px;
|
||||
color: #FFFFFF;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.5px;
|
||||
&:after {
|
||||
content: '\f178';
|
||||
font-family: 'FontAwesome';
|
||||
margin-left: 10px;
|
||||
}
|
||||
&:hover {
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import { LoadingIndicator } from 'strapi-helper-plugin';
|
||||
|
||||
export default Loadable({
|
||||
loader: () => import('./index'),
|
||||
loading: LoadingIndicator,
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* SupportUsTitle
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function SupportUsTitle() {
|
||||
return (
|
||||
<FormattedMessage id="app.components.HomePage.support">
|
||||
{message => <span className={styles.supportUsTitle}>{message}</span>}
|
||||
</FormattedMessage>
|
||||
);
|
||||
}
|
||||
|
||||
export default SupportUsTitle;
|
@ -1,11 +0,0 @@
|
||||
.supportUsTitle {
|
||||
color: #FFFFFF;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.5px;
|
||||
&:after {
|
||||
content: '❤️';
|
||||
margin-left: 7px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
@ -221,6 +221,8 @@ export class Admin extends React.Component {
|
||||
return <PluginDispatcher {...this.props} {...props} {...this.helpers} />;
|
||||
};
|
||||
|
||||
renderRoute = (props, Component) => <Component {...this.props} {...props} />;
|
||||
|
||||
render() {
|
||||
const {
|
||||
admin: { isLoading, showLogoutComponent, showMenu, strapiVersion },
|
||||
@ -256,7 +258,11 @@ export class Admin extends React.Component {
|
||||
{showMenu ? <Header /> : ''}
|
||||
<div className={this.getContentWrapperStyle().sub}>
|
||||
<Switch>
|
||||
<Route path="/" component={HomePage} exact />
|
||||
<Route
|
||||
path="/"
|
||||
render={props => this.renderRoute(props, HomePage)}
|
||||
exact
|
||||
/>
|
||||
<Route
|
||||
path="/plugins/:pluginId"
|
||||
render={this.renderPluginDispatcher}
|
||||
|
@ -1,39 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* BlockLink
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import cn from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function BlockLink({ content, isDocumentation, link, title }) {
|
||||
return (
|
||||
<a
|
||||
className={cn(
|
||||
styles.blockLink,
|
||||
isDocumentation ? styles.blockLinkDocumentation : styles.blockLinkCode
|
||||
)}
|
||||
href={link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FormattedMessage {...title} />
|
||||
<FormattedMessage {...content}>
|
||||
{message => <p>{message}</p>}
|
||||
</FormattedMessage>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
BlockLink.propTypes = {
|
||||
content: PropTypes.object.isRequired,
|
||||
isDocumentation: PropTypes.bool.isRequired,
|
||||
link: PropTypes.string.isRequired,
|
||||
title: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default BlockLink;
|
@ -0,0 +1,50 @@
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { LoadingBar } from 'strapi-helper-plugin';
|
||||
|
||||
const BlogPost = ({ error, isFirst, isLoading, title, content, link }) => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<>
|
||||
<LoadingBar style={{ marginBottom: 13 }} />
|
||||
<LoadingBar style={{ width: '40%', marginBottom: 30 }} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href={`https://blog.strapi.io/${link}`}
|
||||
style={{ color: '#333740' }}
|
||||
>
|
||||
<h2>{title}</h2>
|
||||
<p style={{ marginTop: 17, marginBottom: isFirst ? 32 : 10 }}>
|
||||
{content}
|
||||
</p>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
BlogPost.defaultProps = {
|
||||
content: null,
|
||||
isFirst: false,
|
||||
link: null,
|
||||
title: null,
|
||||
};
|
||||
|
||||
BlogPost.propTypes = {
|
||||
content: PropTypes.string,
|
||||
error: PropTypes.bool.isRequired,
|
||||
isFirst: PropTypes.bool,
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
link: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
};
|
||||
|
||||
export default memo(BlogPost);
|
@ -1,27 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* CommunityContent
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
/* eslint-disable jsx-a11y/accessible-emoji */
|
||||
function CommunityContent() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FormattedMessage id="app.components.HomePage.community.content">
|
||||
{message => (
|
||||
<p className={styles.communityContentP}>
|
||||
{message}
|
||||
</p>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default CommunityContent;
|
@ -1,30 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* CreateContent
|
||||
*
|
||||
*/
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
function CreateContent() {
|
||||
return (
|
||||
<FormattedMessage id="app.components.HomePage.createBlock.content.first">
|
||||
{message => (
|
||||
<p>
|
||||
{message}
|
||||
<span style={{ fontStyle: 'italic', fontWeight: '500' }}>
|
||||
Content Type Builder
|
||||
</span>
|
||||
<FormattedMessage id="app.components.HomePage.createBlock.content.second" />
|
||||
<span style={{ fontStyle: 'italic', fontWeight: '500' }}>
|
||||
"Quick Start"
|
||||
</span>
|
||||
<FormattedMessage id="app.components.HomePage.createBlock.content.tutorial" />
|
||||
</p>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateContent;
|
@ -3,8 +3,7 @@
|
||||
* SocialLink
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Gh from '../../assets/images/social_gh.png';
|
||||
@ -14,7 +13,7 @@ import So from '../../assets/images/social_so.png';
|
||||
import Twitter from '../../assets/images/social_twitter.png';
|
||||
import Reddit from '../../assets/images/social_reddit.png';
|
||||
|
||||
import styles from './styles.scss';
|
||||
import { SocialLinkWrapper } from './components';
|
||||
|
||||
/* eslint-disable jsx-a11y/alt-text */
|
||||
function getSrc(name) {
|
||||
@ -36,36 +35,22 @@ function getSrc(name) {
|
||||
}
|
||||
}
|
||||
|
||||
class SocialLink extends React.PureComponent {
|
||||
state = { imgLoaded: false };
|
||||
|
||||
handleImgLoaded = () => this.setState({ imgLoaded: true });
|
||||
|
||||
render() {
|
||||
const { link, name } = this.props;
|
||||
const { imgLoaded } = this.state;
|
||||
|
||||
return (
|
||||
<div className={cn(styles.socialLink, 'col-md-6 col-lg-6')}>
|
||||
<a href={link} target="_blank" rel="noopener noreferrer">
|
||||
<div>
|
||||
{!imgLoaded && (
|
||||
<div className={styles.spinner}>
|
||||
<div />
|
||||
</div>
|
||||
)}
|
||||
<img src={getSrc(name)} onLoad={this.handleImgLoaded} />
|
||||
</div>
|
||||
<span>{name}</span>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
const SocialLink = ({ link, name }) => {
|
||||
return (
|
||||
<SocialLinkWrapper className="col-6">
|
||||
<a href={link} target="_blank" rel="noopener noreferrer">
|
||||
<div>
|
||||
<img src={getSrc(name)} />
|
||||
</div>
|
||||
<span>{name}</span>
|
||||
</a>
|
||||
</SocialLinkWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
SocialLink.propTypes = {
|
||||
link: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default SocialLink;
|
||||
export default memo(SocialLink);
|
||||
|
@ -1,65 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* WelcomeContent
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
/* eslint-disable jsx-a11y/accessible-emoji */
|
||||
function WelcomeContent({ hasContent }) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className={styles.iconWave}>👋</div>
|
||||
{!hasContent && (
|
||||
<FormattedMessage id="app.components.HomePage.welcomeBlock.content">
|
||||
{message => (
|
||||
<p className={styles.welcomeContentP}>
|
||||
{message}
|
||||
<a
|
||||
className={styles.welcomeContentA}
|
||||
href="https://slack.strapi.io/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Slack
|
||||
</a>
|
||||
<FormattedMessage id="app.components.HomePage.welcomeBlock.content.raise" />
|
||||
<FormattedMessage id="app.components.HomePage.welcomeBlock.content.issues">
|
||||
{message => (
|
||||
<a
|
||||
className={styles.welcomeContentA}
|
||||
href="https://github.com/strapi/strapi/issues/new/choose"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{message}
|
||||
</a>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
</p>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
)}
|
||||
{hasContent && (
|
||||
<FormattedMessage id="app.components.HomePage.welcomeBlock.content.again">
|
||||
{message => <p className={styles.welcomeContentP}>{message}</p>}
|
||||
</FormattedMessage>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
WelcomeContent.defaultProps = {
|
||||
hasContent: false,
|
||||
};
|
||||
|
||||
WelcomeContent.propTypes = {
|
||||
hasContent: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default WelcomeContent;
|
@ -1,39 +0,0 @@
|
||||
import {
|
||||
GET_ARTICLES,
|
||||
GET_ARTICLES_SUCCEEDED,
|
||||
ON_CHANGE,
|
||||
SUBMIT,
|
||||
SUBMIT_SUCCEEDED,
|
||||
} from './constants';
|
||||
|
||||
export function getArticles() {
|
||||
return {
|
||||
type: GET_ARTICLES,
|
||||
};
|
||||
}
|
||||
|
||||
export function getArticlesSucceeded(articles) {
|
||||
return {
|
||||
type: GET_ARTICLES_SUCCEEDED,
|
||||
articles,
|
||||
};
|
||||
}
|
||||
|
||||
export function onChange({ target }) {
|
||||
return {
|
||||
type: ON_CHANGE,
|
||||
value: target.value,
|
||||
};
|
||||
}
|
||||
|
||||
export function submit() {
|
||||
return {
|
||||
type: SUBMIT,
|
||||
};
|
||||
}
|
||||
|
||||
export function submitSucceeded() {
|
||||
return {
|
||||
type: SUBMIT_SUCCEEDED,
|
||||
};
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
const Block = styled.div`
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-bottom: 34px;
|
||||
background: #ffffff;
|
||||
padding: 19px 30px 30px 30px;
|
||||
box-shadow: 0 2px 4px 0 #e3e9f3;
|
||||
border-radius: 3px;
|
||||
line-heigth: 18px;
|
||||
|
||||
a {
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover::after {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 0.3rem;
|
||||
content: '';
|
||||
opacity: 0.1;
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
h2,
|
||||
p {
|
||||
line-height: 18px;
|
||||
}
|
||||
h2 {
|
||||
display: inline-block;
|
||||
}
|
||||
#mainHeader {
|
||||
&:after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
margin-top: 4px;
|
||||
display: block;
|
||||
background: #f0b41e;
|
||||
}
|
||||
}
|
||||
|
||||
.social-wrapper {
|
||||
span {
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
> div:nth-child(2n) {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Container = styled.div`
|
||||
padding: 47px 13px 0 13px;
|
||||
> div {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const P = styled.p`
|
||||
max-width: 550px;
|
||||
padding-top: 10px;
|
||||
padding-right: 30px;
|
||||
color: #5c5f66;
|
||||
font-size: 14px;
|
||||
|
||||
b {
|
||||
font-weight: 600;
|
||||
}
|
||||
`;
|
||||
|
||||
const Wave = styled.div`
|
||||
&:before {
|
||||
content: '👋';
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 30px;
|
||||
font-size: 50px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ALink = styled.a`
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 34px;
|
||||
margin-top: 16px;
|
||||
padding-right: 20px;
|
||||
|
||||
border-radius: 3px;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
line-height: 34px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:before {
|
||||
content: '\f105';
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-right: 10px;
|
||||
font-family: 'FontAwesome';
|
||||
}
|
||||
|
||||
&:hover,
|
||||
focus,
|
||||
active {
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 0.3rem;
|
||||
content: '';
|
||||
opacity: 0.1;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
${({ type }) => {
|
||||
if (type === 'documentation') {
|
||||
return css`
|
||||
padding-left: 20px;
|
||||
color: #ffffff;
|
||||
background-color: #005fea;
|
||||
&:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
`;
|
||||
} else if (type === 'blog') {
|
||||
return css`
|
||||
padding-left: 20px;
|
||||
background-color: #333740;
|
||||
color: #ffffff;
|
||||
&:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
`;
|
||||
} else {
|
||||
return css`
|
||||
color: #005fea;
|
||||
&:hover {
|
||||
color: #005fea;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}}
|
||||
`;
|
||||
|
||||
const Separator = styled.div`
|
||||
height: 2px;
|
||||
background-color: #f7f8f8;
|
||||
`;
|
||||
|
||||
const LinkWrapper = styled.a`
|
||||
width: calc(50% - 6px);
|
||||
position: relative;
|
||||
padding: 21px 30px;
|
||||
padding-left: 95px;
|
||||
|
||||
height: auto;
|
||||
line-height: 18px;
|
||||
background-color: #f7f8f8;
|
||||
&:hover,
|
||||
focus,
|
||||
active {
|
||||
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
${({ type }) => {
|
||||
if (type === 'doc') {
|
||||
return css`
|
||||
content: '\f02d';
|
||||
color: #42b88e;
|
||||
`;
|
||||
}
|
||||
|
||||
return css`
|
||||
content: '\f121';
|
||||
color: #f0811e;
|
||||
`;
|
||||
}}
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
top: 38px;
|
||||
|
||||
|
||||
font-family: 'FontAwesome';
|
||||
font-size: 38px;
|
||||
}
|
||||
|
||||
> p {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
color: #919BAE;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bold {
|
||||
color: #333740
|
||||
font-weight: 600;
|
||||
}
|
||||
`;
|
||||
|
||||
const SocialLinkWrapper = styled.div`
|
||||
height: 54px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
a {
|
||||
width: 100%;
|
||||
color: #333740 !important;
|
||||
text-decoration: none;
|
||||
line-height: 18px;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img {
|
||||
max-height: 25px;
|
||||
max-width: 25px;
|
||||
}
|
||||
span {
|
||||
margin-left: 11px;
|
||||
width: calc(100% - 40px);
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export {
|
||||
ALink,
|
||||
Block,
|
||||
Container,
|
||||
LinkWrapper,
|
||||
P,
|
||||
Separator,
|
||||
SocialLinkWrapper,
|
||||
Wave,
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
export const GET_ARTICLES = 'app/HomePage/GET_ARTICLES';
|
||||
export const GET_ARTICLES_SUCCEEDED = 'app/HomePage/GET_ARTICLES_SUCCEEDED';
|
||||
export const ON_CHANGE = 'app/HomePage/ON_CHANGE';
|
||||
export const SUBMIT = 'app/HomePage/SUBMIT';
|
||||
export const SUBMIT_SUCCEEDED = 'app/HomePage/SUBMIT_SUCCEEDED';
|
55
packages/strapi-admin/admin/src/containers/HomePage/hooks.js
Normal file
55
packages/strapi-admin/admin/src/containers/HomePage/hooks.js
Normal file
@ -0,0 +1,55 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { request } from 'strapi-helper-plugin';
|
||||
|
||||
const useFetch = () => {
|
||||
const [state, setState] = useState({
|
||||
error: false,
|
||||
isLoading: true,
|
||||
posts: [{ link: '1' }, { link: '2' }],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const abortController = new AbortController();
|
||||
const { signal } = abortController;
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await request(
|
||||
'https://blog.strapi.io/ghost/api/v0.1/posts/?client_id=ghost-frontend&client_secret=1f260788b4ec&limit=2',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
signal,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
{ noAuth: true }
|
||||
);
|
||||
const posts = response.posts.reduce((acc, curr) => {
|
||||
acc.push({
|
||||
title: curr.title,
|
||||
link: curr.slug,
|
||||
content: curr.meta_description,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
setState({ isLoading: false, posts, error: false });
|
||||
} catch (err) {
|
||||
setState({ isLoading: false, error: true, posts: [] });
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export default useFetch;
|
@ -4,90 +4,39 @@
|
||||
*
|
||||
*/
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import React, { memo } from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import { createStructuredSelector } from 'reselect';
|
||||
import PropTypes from 'prop-types';
|
||||
import { get, isEmpty, upperFirst } from 'lodash';
|
||||
import cn from 'classnames';
|
||||
import { get, upperFirst } from 'lodash';
|
||||
import { auth } from 'strapi-helper-plugin';
|
||||
|
||||
import useFetch from './hooks';
|
||||
import {
|
||||
Button,
|
||||
InputText as Input,
|
||||
auth,
|
||||
validateInput,
|
||||
} from 'strapi-helper-plugin';
|
||||
|
||||
import Block from '../../components/HomePageBlock';
|
||||
import Sub from '../../components/Sub';
|
||||
import SupportUsCta from '../../components/SupportUsCta';
|
||||
import SupportUsTitle from '../../components/SupportUsTitle';
|
||||
|
||||
import { selectPlugins } from '../App/selectors';
|
||||
|
||||
import injectReducer from '../../utils/injectReducer';
|
||||
import injectSaga from '../../utils/injectSaga';
|
||||
|
||||
import BlockLink from './BlockLink';
|
||||
import CommunityContent from './CommunityContent';
|
||||
import CreateContent from './CreateContent';
|
||||
ALink,
|
||||
Block,
|
||||
Container,
|
||||
LinkWrapper,
|
||||
P,
|
||||
Wave,
|
||||
Separator,
|
||||
} from './components';
|
||||
import BlogPost from './BlogPost';
|
||||
import SocialLink from './SocialLink';
|
||||
import WelcomeContent from './WelcomeContent';
|
||||
|
||||
import { getArticles, onChange, submit } from './actions';
|
||||
import makeSelectHomePage from './selectors';
|
||||
import reducer from './reducer';
|
||||
import saga from './saga';
|
||||
import styles from './styles.scss';
|
||||
|
||||
const FIRST_BLOCK = [
|
||||
{
|
||||
title: {
|
||||
id: 'app.components.HomePage.welcome',
|
||||
},
|
||||
content: () => <WelcomeContent />,
|
||||
},
|
||||
{
|
||||
title: {
|
||||
id: 'app.components.HomePage.create',
|
||||
},
|
||||
content: () => <CreateContent />,
|
||||
},
|
||||
];
|
||||
|
||||
const FIRST_BLOCK_LINKS = [
|
||||
{
|
||||
link: 'https://strapi.io/documentation/',
|
||||
content: {
|
||||
id: 'app.components.BlockLink.documentation.content',
|
||||
},
|
||||
isDocumentation: true,
|
||||
title: {
|
||||
id: 'app.components.BlockLink.documentation',
|
||||
},
|
||||
link:
|
||||
'https://strapi.io/documentation/3.0.0-beta.x/getting-started/quick-start.html#_4-create-a-new-content-type',
|
||||
contentId: 'app.components.BlockLink.documentation.content',
|
||||
titleId: 'app.components.BlockLink.documentation',
|
||||
},
|
||||
{
|
||||
link: 'https://github.com/strapi/strapi-examples',
|
||||
content: {
|
||||
id: 'app.components.BlockLink.code.content',
|
||||
},
|
||||
isDocumentation: false,
|
||||
title: {
|
||||
id: 'app.components.BlockLink.code',
|
||||
},
|
||||
link: 'https://github.com/strapi/foodadvisor',
|
||||
contentId: 'app.components.BlockLink.code.content',
|
||||
titleId: 'app.components.BlockLink.code',
|
||||
},
|
||||
];
|
||||
|
||||
const SECOND_BLOCK = {
|
||||
title: {
|
||||
id: 'app.components.HomePage.community',
|
||||
},
|
||||
content: () => <CommunityContent />,
|
||||
};
|
||||
|
||||
const SOCIAL_LINKS = [
|
||||
{
|
||||
name: 'GitHub',
|
||||
@ -115,196 +64,164 @@ const SOCIAL_LINKS = [
|
||||
},
|
||||
];
|
||||
|
||||
export class HomePage extends React.PureComponent {
|
||||
// eslint-disable-line react/prefer-stateless-function
|
||||
state = { errors: [] };
|
||||
|
||||
componentDidMount() {
|
||||
this.props.getArticles();
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
const HomePage = ({ global: { plugins }, history: { push } }) => {
|
||||
const { error, isLoading, posts } = useFetch();
|
||||
const handleClick = e => {
|
||||
e.preventDefault();
|
||||
const errors = validateInput(
|
||||
this.props.homePage.body.email,
|
||||
{ required: true },
|
||||
'email'
|
||||
);
|
||||
this.setState({ errors });
|
||||
|
||||
if (isEmpty(errors)) {
|
||||
return this.props.submit();
|
||||
}
|
||||
push('/plugins/content-type-builder/models');
|
||||
};
|
||||
const hasAlreadyCreatedContentTypes =
|
||||
get(
|
||||
plugins,
|
||||
['content-manager', 'leftMenuSections', '0', 'links'],
|
||||
[]
|
||||
).filter(contentType => contentType.isDisplayed === true).length > 1;
|
||||
|
||||
showFirstBlock = () =>
|
||||
get(this.context.plugins, 'content-manager.leftMenuSections.0.links', [])
|
||||
.length === 0;
|
||||
const headerId = hasAlreadyCreatedContentTypes
|
||||
? 'HomePage.greetings'
|
||||
: 'app.components.HomePage.welcome';
|
||||
const username = get(auth.getUserInfo(), 'username', '');
|
||||
const linkProps = hasAlreadyCreatedContentTypes
|
||||
? {
|
||||
id: 'app.components.HomePage.button.blog',
|
||||
href: 'https://blog.strapi.io/',
|
||||
onClick: () => {},
|
||||
type: 'blog',
|
||||
target: '_blank',
|
||||
}
|
||||
: {
|
||||
id: 'app.components.HomePage.create',
|
||||
href: '',
|
||||
onClick: handleClick,
|
||||
type: 'documentation',
|
||||
};
|
||||
|
||||
renderButton = () => {
|
||||
/* eslint-disable indent */
|
||||
const data = this.showFirstBlock()
|
||||
? {
|
||||
className: styles.homePageTutorialButton,
|
||||
href:
|
||||
'https://strapi.io/documentation/getting-started/quick-start.html#_3-create-a-content-type',
|
||||
id: 'app.components.HomePage.button.quickStart',
|
||||
primary: true,
|
||||
}
|
||||
: {
|
||||
className: styles.homePageBlogButton,
|
||||
id: 'app.components.HomePage.button.blog',
|
||||
href: 'https://blog.strapi.io/',
|
||||
primary: false,
|
||||
};
|
||||
/* eslint-enable indent */
|
||||
|
||||
return (
|
||||
<a href={data.href} target="_blank">
|
||||
<Button className={data.className} primary={data.primary}>
|
||||
<FormattedMessage id={data.id} />
|
||||
</Button>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
homePage: { articles, body },
|
||||
} = this.props;
|
||||
|
||||
const WELCOME_AGAIN_BLOCK = [
|
||||
{
|
||||
title: {
|
||||
id: 'app.components.HomePage.welcome.again',
|
||||
},
|
||||
name: upperFirst(`${get(auth.getUserInfo(), 'username')}!`),
|
||||
content: () => <WelcomeContent hasContent />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={cn('container-fluid', styles.containerFluid)}>
|
||||
<Helmet title="Home Page" />
|
||||
return (
|
||||
<>
|
||||
<FormattedMessage id="HomePage.helmet.title">
|
||||
{title => <Helmet title={title} />}
|
||||
</FormattedMessage>
|
||||
<Container className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-8 col-lg-8">
|
||||
<div className="col-lg-8 col-md-12">
|
||||
<Block>
|
||||
{this.showFirstBlock() &&
|
||||
FIRST_BLOCK.map((value, key) => (
|
||||
<Sub
|
||||
key={key}
|
||||
{...value}
|
||||
underline={key === 0}
|
||||
bordered={key === 0}
|
||||
/>
|
||||
))}
|
||||
{!this.showFirstBlock() &&
|
||||
WELCOME_AGAIN_BLOCK.concat(articles).map((value, key) => (
|
||||
<Sub
|
||||
key={key}
|
||||
{...value}
|
||||
bordered={key === 0}
|
||||
style={key === 1 ? { marginBottom: '33px' } : {}}
|
||||
underline={key === 0}
|
||||
/>
|
||||
))}
|
||||
{this.renderButton()}
|
||||
<div className={styles.homePageFlex}>
|
||||
{FIRST_BLOCK_LINKS.map((value, key) => (
|
||||
<BlockLink {...value} key={key} />
|
||||
))}
|
||||
</div>
|
||||
</Block>
|
||||
<Block>
|
||||
<Sub {...SECOND_BLOCK} />
|
||||
<div className={styles.homePageFlex}>
|
||||
<div
|
||||
className="row"
|
||||
style={{ width: '100%', marginRight: '0' }}
|
||||
>
|
||||
{SOCIAL_LINKS.map((value, key) => (
|
||||
<SocialLink key={key} {...value} />
|
||||
<Wave />
|
||||
<FormattedMessage
|
||||
id={headerId}
|
||||
values={{
|
||||
name: upperFirst(username),
|
||||
}}
|
||||
>
|
||||
{msg => <h2 id="mainHeader">{msg}</h2>}
|
||||
</FormattedMessage>
|
||||
{hasAlreadyCreatedContentTypes ? (
|
||||
<FormattedMessage id="app.components.HomePage.welcomeBlock.content.again">
|
||||
{msg => <P>{msg}</P>}
|
||||
</FormattedMessage>
|
||||
) : (
|
||||
<FormattedMessage id="HomePage.welcome.congrats">
|
||||
{congrats => {
|
||||
return (
|
||||
<FormattedMessage id="HomePage.welcome.congrats.content">
|
||||
{content => {
|
||||
return (
|
||||
<FormattedMessage id="HomePage.welcome.congrats.content.bold">
|
||||
{boldContent => {
|
||||
return (
|
||||
<P>
|
||||
<b>{congrats}</b>
|
||||
{content}
|
||||
<b>{boldContent}</b>
|
||||
</P>
|
||||
);
|
||||
}}
|
||||
</FormattedMessage>
|
||||
);
|
||||
}}
|
||||
</FormattedMessage>
|
||||
);
|
||||
}}
|
||||
</FormattedMessage>
|
||||
)}
|
||||
{hasAlreadyCreatedContentTypes && (
|
||||
<div style={{ marginTop: isLoading ? 60 : 50 }}>
|
||||
{posts.map((post, index) => (
|
||||
<BlogPost
|
||||
{...post}
|
||||
key={post.link}
|
||||
isFirst={index === 0}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.newsLetterWrapper}>
|
||||
<div>
|
||||
<FormattedMessage id="app.components.HomePage.newsLetter" />
|
||||
</div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className={cn(styles.homePageForm, 'row')}>
|
||||
<div className="col-md-12">
|
||||
<Input
|
||||
value={body.email}
|
||||
onChange={this.props.onChange}
|
||||
name=""
|
||||
placeholder="johndoe@gmail.com"
|
||||
error={!isEmpty(this.state.errors)}
|
||||
/>
|
||||
<FormattedMessage id="app.components.HomePage.cta">
|
||||
{message => <button type="submit">{message}</button>}
|
||||
</FormattedMessage>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
<FormattedMessage id={linkProps.id}>
|
||||
{msg => (
|
||||
<ALink rel="noopener noreferrer" {...linkProps}>
|
||||
{msg}
|
||||
</ALink>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
<Separator style={{ marginTop: 38, marginBottom: 17 }} />
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
{FIRST_BLOCK_LINKS.map((data, index) => {
|
||||
const type = index === 0 ? 'doc' : 'code';
|
||||
|
||||
return (
|
||||
<LinkWrapper
|
||||
href={data.link}
|
||||
target="_blank"
|
||||
key={data.link}
|
||||
type={type}
|
||||
>
|
||||
<FormattedMessage id={data.titleId}>
|
||||
{title => <p className="bold">{title}</p>}
|
||||
</FormattedMessage>
|
||||
<FormattedMessage id={data.contentId}>
|
||||
{content => <p>{content}</p>}
|
||||
</FormattedMessage>
|
||||
</LinkWrapper>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Block>
|
||||
</div>
|
||||
<div className="col-lg-4 col-md-4">
|
||||
<Block className={styles.blockShirt}>
|
||||
<div>
|
||||
<SupportUsTitle />
|
||||
<FormattedMessage id="app.components.HomePage.support.content">
|
||||
{message => <p>{message}</p>}
|
||||
</FormattedMessage>
|
||||
<SupportUsCta />
|
||||
|
||||
<div className="col-4">
|
||||
<Block>
|
||||
<FormattedMessage id="HomePage.community">
|
||||
{msg => <h2>{msg}</h2>}
|
||||
</FormattedMessage>
|
||||
<FormattedMessage id="app.components.HomePage.community.content">
|
||||
{content => <P style={{ marginTop: 7 }}>{content}</P>}
|
||||
</FormattedMessage>
|
||||
<FormattedMessage id="HomePage.roadmap">
|
||||
{msg => (
|
||||
<ALink
|
||||
rel="noopener noreferrer"
|
||||
href="https://portal.productboard.com/strapi/1-public-roadmap/tabs/2-under-consideration"
|
||||
target="_blank"
|
||||
>
|
||||
{msg}
|
||||
</ALink>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
<div
|
||||
className="row social-wrapper"
|
||||
style={{ display: 'flex', margin: 0, marginTop: 43 }}
|
||||
>
|
||||
{SOCIAL_LINKS.map((value, key) => (
|
||||
<SocialLink key={key} {...value} />
|
||||
))}
|
||||
</div>
|
||||
</Block>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
HomePage.contextTypes = {
|
||||
plugins: PropTypes.object,
|
||||
};
|
||||
|
||||
HomePage.propTypes = {
|
||||
getArticles: PropTypes.func.isRequired,
|
||||
homePage: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
submit: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
homePage: makeSelectHomePage(),
|
||||
plugins: selectPlugins(),
|
||||
});
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators(
|
||||
{
|
||||
getArticles,
|
||||
onChange,
|
||||
submit,
|
||||
},
|
||||
dispatch
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const withConnect = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
);
|
||||
|
||||
const withReducer = injectReducer({ key: 'homePage', reducer });
|
||||
const withSaga = injectSaga({ key: 'homePage', saga });
|
||||
|
||||
// export default connect(mapDispatchToProps)(HomePage);
|
||||
export default compose(
|
||||
withReducer,
|
||||
withSaga,
|
||||
withConnect
|
||||
)(HomePage);
|
||||
export default memo(HomePage);
|
||||
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"welcome": {
|
||||
"id": "app.components.HomePage.welcome",
|
||||
"defaultMessage": "Welcome on board!"
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* HomePage reducer
|
||||
*/
|
||||
|
||||
import { fromJS, List, Map } from 'immutable';
|
||||
|
||||
import { GET_ARTICLES_SUCCEEDED, ON_CHANGE, SUBMIT_SUCCEEDED } from './constants';
|
||||
|
||||
const initialState = fromJS({
|
||||
articles: List([
|
||||
{content: '', title: '', link: ''},
|
||||
{content: '', title: '', link: ''},
|
||||
]),
|
||||
body: Map({
|
||||
email: '',
|
||||
}),
|
||||
});
|
||||
|
||||
function homePageReducer(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case GET_ARTICLES_SUCCEEDED:
|
||||
return state.update('articles', () => List(action.articles));
|
||||
case ON_CHANGE:
|
||||
return state.updateIn(['body', 'email'], () => action.value);
|
||||
case SUBMIT_SUCCEEDED:
|
||||
return state.updateIn(['body', 'email'], () => '');
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default homePageReducer;
|
@ -1,67 +0,0 @@
|
||||
/* eslint-disable */
|
||||
import 'whatwg-fetch';
|
||||
import { dropRight, take } from 'lodash';
|
||||
import removeMd from 'remove-markdown';
|
||||
import { all, call, fork, put, select, takeLatest } from 'redux-saga/effects';
|
||||
import { request } from 'strapi-helper-plugin';
|
||||
import { getArticlesSucceeded, submitSucceeded } from './actions';
|
||||
import { GET_ARTICLES, SUBMIT } from './constants';
|
||||
import { makeSelectBody } from './selectors';
|
||||
|
||||
function* getArticles() {
|
||||
try {
|
||||
const articles = yield call(fetchArticles);
|
||||
const posts = articles.posts.reduce((acc, curr) => {
|
||||
// Limit to 200 characters and remove last word.
|
||||
const content = dropRight(
|
||||
take(removeMd(curr.markdown), 250)
|
||||
.join('')
|
||||
.split(' ')
|
||||
).join(' ');
|
||||
|
||||
acc.push({
|
||||
title: curr.title,
|
||||
link: curr.slug,
|
||||
content: `${content} [...]`,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
yield put(getArticlesSucceeded(posts));
|
||||
} catch (err) {
|
||||
// Silent
|
||||
}
|
||||
}
|
||||
|
||||
function* submit() {
|
||||
try {
|
||||
const body = yield select(makeSelectBody());
|
||||
yield call(request, 'https://analytics.strapi.io/register', {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
} catch (err) {
|
||||
// silent
|
||||
} finally {
|
||||
strapi.notification.success('HomePage.notification.newsLetter.success');
|
||||
yield put(submitSucceeded());
|
||||
}
|
||||
}
|
||||
|
||||
function* defaultSaga() {
|
||||
yield all([
|
||||
fork(takeLatest, SUBMIT, submit),
|
||||
fork(takeLatest, GET_ARTICLES, getArticles),
|
||||
]);
|
||||
}
|
||||
|
||||
function fetchArticles() {
|
||||
return fetch(
|
||||
'https://blog.strapi.io/ghost/api/v0.1/posts/?client_id=ghost-frontend&client_secret=1f260788b4ec&limit=2',
|
||||
{}
|
||||
).then(resp => {
|
||||
return resp.json ? resp.json() : resp;
|
||||
});
|
||||
}
|
||||
export default defaultSaga;
|
@ -1,32 +0,0 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
/**
|
||||
* Direct selector to the homePage state domain
|
||||
*/
|
||||
const selectHomePageDomain = () => (state) => state.get('homePage');
|
||||
|
||||
/**
|
||||
* Other specific selectors
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Default selector used by HomePage
|
||||
*/
|
||||
|
||||
const makeSelectHomePage = () => createSelector(
|
||||
selectHomePageDomain(),
|
||||
(substate) => substate.toJS(),
|
||||
);
|
||||
|
||||
const makeSelectBody = () => createSelector(
|
||||
selectHomePageDomain(),
|
||||
(substate) => substate.get('body').toJS(),
|
||||
);
|
||||
|
||||
|
||||
export default makeSelectHomePage;
|
||||
export {
|
||||
makeSelectBody,
|
||||
selectHomePageDomain,
|
||||
};
|
@ -1,289 +0,0 @@
|
||||
.blockLink {
|
||||
position: relative;
|
||||
width: calc(50% - 6px);
|
||||
height: auto;
|
||||
margin-top: 41px;
|
||||
padding: 22px 25px 19px 96px;
|
||||
background: #F7F8F8;
|
||||
border-radius: 3px;
|
||||
line-height: 18px;
|
||||
text-decoration: none;
|
||||
> span {
|
||||
font-family: Lato-Bold;
|
||||
font-size: 16px;
|
||||
color: #333740;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
> p {
|
||||
font-family: Lato-Regular;
|
||||
font-size: 13px;
|
||||
color: #919BAE;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
margin: 0;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.blockLinkDocumentation {
|
||||
&:before {
|
||||
content: '\f02d';
|
||||
position: absolute;
|
||||
left: 3rem;
|
||||
top: 4rem;
|
||||
color: #42B88E;
|
||||
font-family: 'FontAwesome';
|
||||
font-size: 38px;
|
||||
}
|
||||
}
|
||||
|
||||
.blockLinkCode {
|
||||
&:before {
|
||||
content: '\f121';
|
||||
position: absolute;
|
||||
left: 3rem;
|
||||
top: 4rem;
|
||||
color: #F0811E;
|
||||
font-family: 'FontAwesome';
|
||||
font-size: 38px;
|
||||
}
|
||||
}
|
||||
|
||||
.blockShirt {
|
||||
position: relative;
|
||||
min-height: 34rem;
|
||||
margin-bottom: 20px;
|
||||
background-image: linear-gradient(45deg, #1A67DA 0%, #0097F6 100%) !important;
|
||||
line-height: 18px;
|
||||
> div {
|
||||
position: absolute;
|
||||
padding: 38px 0 62px 25px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
color: #FFFFFF;
|
||||
|
||||
> p {
|
||||
max-width: 400px;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 125px;
|
||||
padding-right: 35px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
&:before {
|
||||
opacity: 0.7;
|
||||
content: '';
|
||||
background-image: url('../../assets/images/bg_hp_tee_shirt.png') !important;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
.communityContentP {
|
||||
display: block;
|
||||
max-width: 49rem !important;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 51px;
|
||||
color: #919BAE !important;
|
||||
}
|
||||
|
||||
.containerFluid {
|
||||
padding: 47px 13px 0 13px;
|
||||
> div {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.homePageFlex {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.homePageForm {
|
||||
padding-top: 19px;
|
||||
padding-left: 15px;
|
||||
|
||||
div {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
float: left;
|
||||
width: calc(100% - 120px);
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
&:focus {
|
||||
border-color: #E3E9F3;
|
||||
}
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
float: left;
|
||||
min-width: 100px;
|
||||
height: 3.4rem;
|
||||
margin-top: .9rem;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
text-align: center;
|
||||
background: #333740;
|
||||
color: #FFFFFF;
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
}
|
||||
|
||||
.homePageTutorialButton {
|
||||
position: relative;
|
||||
height: 34px;
|
||||
margin-top: 17px;
|
||||
margin-bottom: 1px;
|
||||
padding-left: 40px;
|
||||
padding-right: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
line-height: 33px;
|
||||
letter-spacing: 0.46px;
|
||||
text-align: left;
|
||||
&:before {
|
||||
content: '\f105';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 20px;
|
||||
font-size: 22px;
|
||||
margin-right: 10px;
|
||||
font-family: 'FontAwesome';
|
||||
}
|
||||
}
|
||||
|
||||
.homePageBlogButton {
|
||||
position: relative;
|
||||
height: 34px;
|
||||
margin-top: 17px;
|
||||
margin-bottom: 1px;
|
||||
padding-left: 40px;
|
||||
padding-right: 20px;
|
||||
background: #333740;
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
line-height: 33px;
|
||||
letter-spacing: 0.46px;
|
||||
text-align: left;
|
||||
&:before {
|
||||
content: '\f105';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 20px;
|
||||
font-size: 22px;
|
||||
margin-right: 10px;
|
||||
font-family: 'FontAwesome';
|
||||
}
|
||||
}
|
||||
|
||||
.iconWave {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 0px;
|
||||
font-size: 50px;
|
||||
}
|
||||
|
||||
.newsLetterWrapper {
|
||||
height: auto;
|
||||
min-width: 50%;
|
||||
padding: 20px 20px;
|
||||
background: #F7F8F8;
|
||||
border-radius: 3px;
|
||||
line-height: 18px;
|
||||
> div {
|
||||
padding-right: 50px;
|
||||
> span {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.socialLink {
|
||||
height: 54px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
a {
|
||||
color: #333740 !important;
|
||||
text-decoration: none;
|
||||
line-height: 18px;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img {
|
||||
max-height: 25px;
|
||||
max-width: 25px;
|
||||
}
|
||||
span {
|
||||
margin-left: 11px;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.welcomeContentA {
|
||||
color: #005FEA;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.welcomeContentP {
|
||||
display: block;
|
||||
max-width: 55rem !important;
|
||||
margin-bottom: 31px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
> div {
|
||||
border: 2px solid #f3f3f3; /* Light grey */
|
||||
border-top: 2px solid #3498db; /* Blue */
|
||||
border-radius: 50%;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
@ -154,5 +154,12 @@
|
||||
"notification.error": "An error occurred",
|
||||
"notification.error.layout": "Couldn't retrieve the layout",
|
||||
"request.error.model.unknown": "This model doesn't exist",
|
||||
"app.utils.delete": "Delete"
|
||||
"app.utils.delete": "Delete",
|
||||
"HomePage.helmet.title": "Homepage",
|
||||
"HomePage.welcome.congrats": "Congrats!",
|
||||
"HomePage.welcome.congrats.content": "You are logged as the first administrator. To discover the powerful features provided by Strapi,",
|
||||
"HomePage.welcome.congrats.content.bold": "we recommend you to create your first Content-Type.",
|
||||
"HomePage.community": "Join the Community",
|
||||
"HomePage.roadmap": "See our roadmap",
|
||||
"HomePage.greetings": "Hi {name}!"
|
||||
}
|
||||
|
@ -154,5 +154,12 @@
|
||||
"notification.error": "Une erreur est survenue",
|
||||
"notification.error.layout": "Impossible de récupérer le layout de l'admin",
|
||||
"request.error.model.unknown": "Le model n'existe pas",
|
||||
"app.utils.delete": "Supprimer"
|
||||
"app.utils.delete": "Supprimer",
|
||||
"HomePage.helmet.title": "Accueil",
|
||||
"HomePage.welcome.congrats": "Bravo!",
|
||||
"HomePage.welcome.congrats.content": "Vous êtes connecté en tant que premier Administrateur. Afin de découvrir les fonctionnalités proposées par Strapi,",
|
||||
"HomePage.welcome.congrats.content.bold": "nous vous conseillons de créer votre premier Type de Contenu.",
|
||||
"HomePage.community": "Rejoignez la Communauté",
|
||||
"HomePage.roadmap": "Regardez notre roadmap",
|
||||
"HomePage.greetings": "Bonjour {name}!"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user