feat: simplify landing page
This commit is contained in:
committed by
Mrugesh Mohapatra
parent
5dd8044035
commit
9ff7bf5801
@ -83,9 +83,9 @@ export const loginRedirect = () => {
|
|||||||
const successRedirect = req => {
|
const successRedirect = req => {
|
||||||
if (!!req && req.session && req.session.returnTo) {
|
if (!!req && req.session && req.session.returnTo) {
|
||||||
delete req.session.returnTo;
|
delete req.session.returnTo;
|
||||||
return `${homeLocation}/welcome`;
|
return `${homeLocation}/`;
|
||||||
}
|
}
|
||||||
return `${homeLocation}/welcome`;
|
return `${homeLocation}/`;
|
||||||
};
|
};
|
||||||
|
|
||||||
let redirect = url.parse(successRedirect(req), true);
|
let redirect = url.parse(successRedirect(req), true);
|
||||||
@ -112,7 +112,7 @@ export const createPassportCallbackAuthenticator = (strategy, config) => (
|
|||||||
if (!user || !userInfo) {
|
if (!user || !userInfo) {
|
||||||
return res.redirect('/signin');
|
return res.redirect('/signin');
|
||||||
}
|
}
|
||||||
const redirect = `${homeLocation}/welcome`;
|
const redirect = `${homeLocation}/`;
|
||||||
|
|
||||||
const { accessToken } = userInfo;
|
const { accessToken } = userInfo;
|
||||||
const { provider } = config;
|
const { provider } = config;
|
||||||
|
@ -3,7 +3,7 @@ import { homeLocation, apiLocation } from '../../config/env';
|
|||||||
|
|
||||||
const { clientID, clientSecret, domain } = auth0;
|
const { clientID, clientSecret, domain } = auth0;
|
||||||
|
|
||||||
const successRedirect = `${homeLocation}/welcome`;
|
const successRedirect = `${homeLocation}/`;
|
||||||
const failureRedirect = `${homeLocation}/signin`;
|
const failureRedirect = `${homeLocation}/signin`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -55,7 +55,7 @@ export function ifNotVerifiedRedirectToUpdateEmail(req, res, next) {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ifUserRedirectTo(path = `${homeLocation}/welcome`, status) {
|
export function ifUserRedirectTo(path = `${homeLocation}/`, status) {
|
||||||
status = status === 302 ? 302 : 301;
|
status = status === 302 ? 302 : 301;
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
const { accessToken } = getAccessTokenFromRequest(req);
|
const { accessToken } = getAccessTokenFromRequest(req);
|
||||||
|
@ -54,7 +54,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.donation-email-container input {
|
.donation-email-container input {
|
||||||
color: var(--secondary-background);
|
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ const createOnClick = (navigate, isSignedIn) => e => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
gtagReportConversion();
|
gtagReportConversion();
|
||||||
if (isSignedIn) {
|
if (isSignedIn) {
|
||||||
return gatsbyNavigate('/welcome');
|
return gatsbyNavigate('/');
|
||||||
}
|
}
|
||||||
return navigate(`${apiLocation}/signin`);
|
return navigate(`${apiLocation}/signin`);
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@ function Header(props) {
|
|||||||
<Link className='home-link' to='/'>
|
<Link className='home-link' to='/'>
|
||||||
<NavLogo />
|
<NavLogo />
|
||||||
</Link>
|
</Link>
|
||||||
{disableSettings ? null : <SearchBar />}
|
<SearchBar />
|
||||||
<NavigationMenu disableSettings={disableSettings} />
|
<NavigationMenu disableSettings={disableSettings} />
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
25
client/src/components/landing/Landing.test.js
Normal file
25
client/src/components/landing/Landing.test.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* global expect */
|
||||||
|
import React from 'react';
|
||||||
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
|
|
||||||
|
import { IndexPage } from '../../pages';
|
||||||
|
|
||||||
|
describe('<Landing />', () => {
|
||||||
|
it('renders when visiting index page and logged out', () => {
|
||||||
|
const shallow = new ShallowRenderer();
|
||||||
|
shallow.render(<IndexPage {...loggedOutProps} />);
|
||||||
|
const result = shallow.getRenderOutput();
|
||||||
|
expect(result.type.displayName === 'Landing').toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const loggedOutProps = {
|
||||||
|
fetchState: {
|
||||||
|
complete: true,
|
||||||
|
error: null,
|
||||||
|
errored: false,
|
||||||
|
pending: false
|
||||||
|
},
|
||||||
|
isSignedIn: false,
|
||||||
|
user: {}
|
||||||
|
};
|
72
client/src/components/landing/index.js
Normal file
72
client/src/components/landing/index.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { Grid, Row, Col, Image } from '@freecodecamp/react-bootstrap';
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
|
||||||
|
import Login from '../Header/components/Login';
|
||||||
|
import { Spacer } from '../helpers';
|
||||||
|
|
||||||
|
import './landing.css';
|
||||||
|
|
||||||
|
const BigCallToAction = () => (
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<Login block={true}>Sign in and get started.</Login>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
|
||||||
|
function Landing() {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Helmet>
|
||||||
|
<title>Learn to code | freeCodeCamp.org</title>
|
||||||
|
</Helmet>
|
||||||
|
<main className='index-page'>
|
||||||
|
<Spacer size={2} />
|
||||||
|
<Grid>
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<h1 className='big-heading text-center'>
|
||||||
|
Welcome to freeCodeCamp.org
|
||||||
|
</h1>
|
||||||
|
<Spacer />
|
||||||
|
<h2 className='medium-heading'>Learn to code.</h2>
|
||||||
|
<h2 className='medium-heading'>
|
||||||
|
Build projects and earn certifications.
|
||||||
|
</h2>
|
||||||
|
<h2 className='medium-heading'>
|
||||||
|
Grow your portfolio and get a developer job.
|
||||||
|
</h2>
|
||||||
|
<h2 className='medium-heading'>
|
||||||
|
It's all 100% free thanks to our nonprofit's donors.
|
||||||
|
</h2>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spacer />
|
||||||
|
<BigCallToAction />
|
||||||
|
<Spacer size={2} />
|
||||||
|
<Image
|
||||||
|
alt='companies featuring freeCodeCamp.org'
|
||||||
|
className='img-center'
|
||||||
|
responsive={true}
|
||||||
|
src='https://cdn-media-1.freecodecamp.org/learn/as-seen-on.png'
|
||||||
|
/>
|
||||||
|
<Spacer />
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<h2 className='medium-heading'>
|
||||||
|
Since 2014, more than 40,000 freeCodeCamp.org graduates have
|
||||||
|
gotten jobs in tech.
|
||||||
|
</h2>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spacer />
|
||||||
|
</Grid>
|
||||||
|
</main>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Landing.displayName = 'Landing';
|
||||||
|
|
||||||
|
export default Landing;
|
@ -25,7 +25,6 @@ import Header from '../Header';
|
|||||||
import Footer from '../Footer';
|
import Footer from '../Footer';
|
||||||
|
|
||||||
import './global.css';
|
import './global.css';
|
||||||
import './layout.css';
|
|
||||||
import './variables.css';
|
import './variables.css';
|
||||||
|
|
||||||
fontawesome.config = {
|
fontawesome.config = {
|
||||||
@ -67,7 +66,6 @@ const propTypes = {
|
|||||||
hasMessage: PropTypes.bool,
|
hasMessage: PropTypes.bool,
|
||||||
isOnline: PropTypes.bool.isRequired,
|
isOnline: PropTypes.bool.isRequired,
|
||||||
isSignedIn: PropTypes.bool,
|
isSignedIn: PropTypes.bool,
|
||||||
landingPage: PropTypes.bool,
|
|
||||||
onlineStatusChange: PropTypes.func.isRequired,
|
onlineStatusChange: PropTypes.func.isRequired,
|
||||||
pathname: PropTypes.string.isRequired,
|
pathname: PropTypes.string.isRequired,
|
||||||
removeFlashMessage: PropTypes.func.isRequired,
|
removeFlashMessage: PropTypes.func.isRequired,
|
||||||
@ -133,7 +131,6 @@ class DefaultLayout extends Component {
|
|||||||
flashMessage,
|
flashMessage,
|
||||||
isOnline,
|
isOnline,
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
landingPage,
|
|
||||||
removeFlashMessage,
|
removeFlashMessage,
|
||||||
showFooter = true,
|
showFooter = true,
|
||||||
theme
|
theme
|
||||||
@ -157,11 +154,8 @@ class DefaultLayout extends Component {
|
|||||||
<style>{fontawesome.dom.css()}</style>
|
<style>{fontawesome.dom.css()}</style>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<WithInstantSearch>
|
<WithInstantSearch>
|
||||||
<Header disableSettings={landingPage} />
|
<Header disableSettings={!isSignedIn} />
|
||||||
<div
|
<div className={`default-layout`}>
|
||||||
className={`default-layout
|
|
||||||
${landingPage ? 'landing-page' : ''}`}
|
|
||||||
>
|
|
||||||
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
||||||
{hasMessage && flashMessage ? (
|
{hasMessage && flashMessage ? (
|
||||||
<Flash flashMessage={flashMessage} onClose={removeFlashMessage} />
|
<Flash flashMessage={flashMessage} onClose={removeFlashMessage} />
|
||||||
|
@ -14,10 +14,6 @@ body {
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.big-heading {
|
|
||||||
font-size: 50px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.default-layout {
|
.default-layout {
|
||||||
margin-top: var(--header-height);
|
margin-top: var(--header-height);
|
||||||
background: var(--secondary-background);
|
background: var(--secondary-background);
|
||||||
@ -62,6 +58,33 @@ th {
|
|||||||
margin: 0 0 1.2rem;
|
margin: 0 0 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.big-heading {
|
||||||
|
font-size: 2.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.medium-heading {
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.big-heading {
|
||||||
|
font-size: 1.6rem !important;
|
||||||
|
}
|
||||||
|
.medium-heading {
|
||||||
|
font-size: 1.4rem !important;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.text-center {
|
.text-center {
|
||||||
text-align: center !important;
|
text-align: center !important;
|
||||||
}
|
}
|
||||||
@ -351,3 +374,9 @@ pre {
|
|||||||
.nav-tabs > li > a {
|
.nav-tabs > li > a {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blockquote footer,
|
||||||
|
blockquote small,
|
||||||
|
blockquote .small {
|
||||||
|
color: var(--gray-45);
|
||||||
|
}
|
||||||
|
@ -1,606 +0,0 @@
|
|||||||
/* html {
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-moz-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
article,
|
|
||||||
aside,
|
|
||||||
details,
|
|
||||||
figcaption,
|
|
||||||
figure,
|
|
||||||
footer,
|
|
||||||
header,
|
|
||||||
main,
|
|
||||||
menu,
|
|
||||||
nav,
|
|
||||||
section,
|
|
||||||
summary {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
audio,
|
|
||||||
canvas,
|
|
||||||
progress,
|
|
||||||
video {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
audio:not([controls]) {
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
progress {
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
[hidden],
|
|
||||||
template {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
a:active,
|
|
||||||
a:hover {
|
|
||||||
outline-width: 0;
|
|
||||||
}
|
|
||||||
abbr[title] {
|
|
||||||
border-bottom: none;
|
|
||||||
text-decoration: underline;
|
|
||||||
text-decoration: underline dotted;
|
|
||||||
}
|
|
||||||
b,
|
|
||||||
strong {
|
|
||||||
font-weight: inherit;
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
dfn {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 2em;
|
|
||||||
margin: 0.67em 0;
|
|
||||||
}
|
|
||||||
mark {
|
|
||||||
background-color: #ff0;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
small {
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
sub,
|
|
||||||
sup {
|
|
||||||
font-size: 75%;
|
|
||||||
line-height: 0;
|
|
||||||
position: relative;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
sub {
|
|
||||||
bottom: -0.25em;
|
|
||||||
}
|
|
||||||
sup {
|
|
||||||
top: -0.5em;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
border-style: none;
|
|
||||||
}
|
|
||||||
svg:not(:root) {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
code,
|
|
||||||
kbd,
|
|
||||||
pre,
|
|
||||||
samp {
|
|
||||||
font-family: monospace, monospace;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
figure {
|
|
||||||
margin: 1em 40px;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
box-sizing: content-box;
|
|
||||||
height: 0;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
button,
|
|
||||||
input,
|
|
||||||
optgroup,
|
|
||||||
select,
|
|
||||||
textarea {
|
|
||||||
font: inherit;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
optgroup {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
button,
|
|
||||||
input {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
button,
|
|
||||||
select {
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
[type='button']::-moz-focus-inner,
|
|
||||||
[type='reset']::-moz-focus-inner,
|
|
||||||
[type='submit']::-moz-focus-inner,
|
|
||||||
button::-moz-focus-inner {
|
|
||||||
border-style: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
[type='button']:-moz-focusring,
|
|
||||||
[type='reset']:-moz-focusring,
|
|
||||||
[type='submit']:-moz-focusring,
|
|
||||||
button:-moz-focusring {
|
|
||||||
outline: 1px dotted ButtonText;
|
|
||||||
}
|
|
||||||
fieldset {
|
|
||||||
border: 1px solid silver;
|
|
||||||
margin: 0 2px;
|
|
||||||
padding: 0.35em 0.625em 0.75em;
|
|
||||||
}
|
|
||||||
legend {
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: inherit;
|
|
||||||
display: table;
|
|
||||||
max-width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
textarea {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
[type='checkbox'],
|
|
||||||
[type='radio'] {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
[type='number']::-webkit-inner-spin-button,
|
|
||||||
[type='number']::-webkit-outer-spin-button {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
[type='search'] {
|
|
||||||
-webkit-appearance: textfield;
|
|
||||||
outline-offset: -2px;
|
|
||||||
}
|
|
||||||
[type='search']::-webkit-search-cancel-button,
|
|
||||||
[type='search']::-webkit-search-decoration {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
::-webkit-input-placeholder {
|
|
||||||
color: inherit;
|
|
||||||
opacity: 0.54;
|
|
||||||
}
|
|
||||||
::-webkit-file-upload-button {
|
|
||||||
-webkit-appearance: button;
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
html {
|
|
||||||
font: 112.5%/1.45em georgia, serif;
|
|
||||||
box-sizing: border-box;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
* {
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
*:before {
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
*:after {
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
color: hsla(0, 0%, 0%, 0.8);
|
|
||||||
font-family: 'Lato', sans-serif;
|
|
||||||
font-weight: normal;
|
|
||||||
word-wrap: break-word;
|
|
||||||
font-kerning: normal;
|
|
||||||
-moz-font-feature-settings: 'kern', 'liga', 'clig', 'calt';
|
|
||||||
-webkit-font-feature-settings: 'kern', 'liga', 'clig', 'calt';
|
|
||||||
font-feature-settings: 'kern', 'liga', 'clig', 'calt';
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
color: inherit;
|
|
||||||
font-family: 'Lato', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-size: 2.25rem;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
color: inherit;
|
|
||||||
font-family: 'Lato', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-size: 1.62671rem;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
color: inherit;
|
|
||||||
font-family: 'Lato', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-size: 1.38316rem;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
color: inherit;
|
|
||||||
font-family: 'Lato', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
h5 {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
color: inherit;
|
|
||||||
font-family: 'Lato', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-size: 0.85028rem;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
h6 {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
color: inherit;
|
|
||||||
font-family: 'Lato', -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|
||||||
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-size: 0.78405rem;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
hgroup {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
margin-left: 1.45rem;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
list-style-position: outside;
|
|
||||||
list-style-image: none;
|
|
||||||
}
|
|
||||||
ol {
|
|
||||||
margin-left: 1.45rem;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
list-style-position: outside;
|
|
||||||
list-style-image: none;
|
|
||||||
}
|
|
||||||
dl {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
dd {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
figure {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
line-height: 1.42;
|
|
||||||
background: hsla(0, 0%, 0%, 0.04);
|
|
||||||
border-radius: 3px;
|
|
||||||
overflow: auto;
|
|
||||||
word-wrap: normal;
|
|
||||||
padding: 1.45rem;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
line-height: 1.45rem;
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
fieldset {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
blockquote {
|
|
||||||
margin-left: 1.45rem;
|
|
||||||
margin-right: 1.45rem;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
form {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
noscript {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
iframe {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: calc(1.45rem - 1px);
|
|
||||||
background-image: linear-gradient(
|
|
||||||
to right,
|
|
||||||
rgba(0, 0, 0, 0),
|
|
||||||
rgba(0, 0, 0, 0.75),
|
|
||||||
rgba(0, 0, 0, 0)
|
|
||||||
);
|
|
||||||
border: none;
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
address {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
margin-bottom: 1.45rem;
|
|
||||||
}
|
|
||||||
b {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
dt {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
margin-bottom: calc(1.45rem / 2);
|
|
||||||
}
|
|
||||||
ol li {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
ul li {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
li > ol {
|
|
||||||
margin-left: 1.45rem;
|
|
||||||
margin-bottom: calc(1.45rem / 2);
|
|
||||||
margin-top: calc(1.45rem / 2);
|
|
||||||
}
|
|
||||||
li > ul {
|
|
||||||
margin-left: 1.45rem;
|
|
||||||
margin-bottom: calc(1.45rem / 2);
|
|
||||||
margin-top: calc(1.45rem / 2);
|
|
||||||
}
|
|
||||||
blockquote *:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
li *:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
p *:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
li > p {
|
|
||||||
margin-bottom: calc(1.45rem / 2);
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
line-height: 1.45rem;
|
|
||||||
}
|
|
||||||
kbd {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
line-height: 1.45rem;
|
|
||||||
}
|
|
||||||
samp {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
line-height: 1.45rem;
|
|
||||||
}
|
|
||||||
abbr {
|
|
||||||
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
acronym {
|
|
||||||
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
abbr[title] {
|
|
||||||
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
|
|
||||||
cursor: help;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
thead {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
td,
|
|
||||||
th {
|
|
||||||
text-align: left;
|
|
||||||
border-bottom: 1px solid hsla(0, 0%, 0%, 0.12);
|
|
||||||
font-feature-settings: 'tnum';
|
|
||||||
-moz-font-feature-settings: 'tnum';
|
|
||||||
-webkit-font-feature-settings: 'tnum';
|
|
||||||
padding-left: 0.96667rem;
|
|
||||||
padding-right: 0.96667rem;
|
|
||||||
padding-top: 0.725rem;
|
|
||||||
padding-bottom: calc(0.725rem - 1px);
|
|
||||||
}
|
|
||||||
th:first-child,
|
|
||||||
td:first-child {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
th:last-child,
|
|
||||||
td:last-child {
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
tt,
|
|
||||||
code {
|
|
||||||
background-color: hsla(0, 0%, 0%, 0.04);
|
|
||||||
border-radius: 3px;
|
|
||||||
font-family: 'SFMono-Regular', Consolas, 'Roboto Mono', 'Droid Sans Mono',
|
|
||||||
'Liberation Mono', Menlo, Courier, monospace;
|
|
||||||
padding: 0.2em 0.3em;
|
|
||||||
}
|
|
||||||
pre code {
|
|
||||||
background: none;
|
|
||||||
line-height: 1.42;
|
|
||||||
}
|
|
||||||
@media only screen and (max-width: 480px) {
|
|
||||||
html {
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.default-layout {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-top: var(--header-height);
|
|
||||||
min-height: calc(100vh - var(--header-height));
|
|
||||||
} */
|
|
37
client/src/components/welcome/Welcome.test.js
Normal file
37
client/src/components/welcome/Welcome.test.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* global expect */
|
||||||
|
import React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
|
import 'jest-dom/extend-expect';
|
||||||
|
import { IndexPage } from '../../pages';
|
||||||
|
|
||||||
|
import Welcome from './';
|
||||||
|
|
||||||
|
describe('<Welcome />', () => {
|
||||||
|
it('renders when visiting index page and logged in', () => {
|
||||||
|
const container = renderer
|
||||||
|
.create(<IndexPage {...loggedInProps} />)
|
||||||
|
.toTree();
|
||||||
|
expect(container.rendered.type.displayName === 'Welcome').toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has four links', () => {
|
||||||
|
const container = renderer.create(<Welcome name={'developmentuser'} />)
|
||||||
|
.root;
|
||||||
|
expect(container.findAllByType('a').length === 4).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const loggedInProps = {
|
||||||
|
fetchState: {
|
||||||
|
complete: true,
|
||||||
|
error: null,
|
||||||
|
errored: false,
|
||||||
|
pending: false
|
||||||
|
},
|
||||||
|
isSignedIn: true,
|
||||||
|
user: {
|
||||||
|
acceptedPrivacyTerms: true,
|
||||||
|
username: 'developmentuser'
|
||||||
|
}
|
||||||
|
};
|
88
client/src/components/welcome/index.js
Normal file
88
client/src/components/welcome/index.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import { Grid, Row, Col } from '@freecodecamp/react-bootstrap';
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { Spacer, Link } from '../helpers';
|
||||||
|
import { randomQuote } from '../../utils/get-words';
|
||||||
|
|
||||||
|
import './welcome.css';
|
||||||
|
|
||||||
|
function Welcome({ name }) {
|
||||||
|
const { quote, author } = randomQuote();
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Helmet>
|
||||||
|
<title>Welcome | freeCodeCamp.org</title>
|
||||||
|
</Helmet>
|
||||||
|
<main>
|
||||||
|
<Grid>
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<Spacer />
|
||||||
|
<h1 className='text-center big-heading'>
|
||||||
|
Welcome {name ? name : 'Camper'}!
|
||||||
|
</h1>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spacer />
|
||||||
|
<Row className='text-center quote-partial'>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<blockquote className='blockquote'>
|
||||||
|
<span>
|
||||||
|
<q>{quote}</q>
|
||||||
|
<footer className='quote-author blockquote-footer'>
|
||||||
|
<cite>{author}</cite>
|
||||||
|
</footer>
|
||||||
|
</span>
|
||||||
|
</blockquote>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<Spacer />
|
||||||
|
<h2 className='text-center medium-heading'>
|
||||||
|
What would you like to do today?
|
||||||
|
</h2>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spacer />
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<Link className='btn btn-lg btn-primary btn-block' to='/learn'>
|
||||||
|
Build Projects and Earn Certifications
|
||||||
|
</Link>
|
||||||
|
<Link className='btn btn-lg btn-primary btn-block' to='settings'>
|
||||||
|
Update Your Developer Portfolio
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className='btn btn-lg btn-primary btn-block'
|
||||||
|
external={true}
|
||||||
|
to='/news'
|
||||||
|
>
|
||||||
|
Read Developer News
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className='btn btn-lg btn-primary btn-block'
|
||||||
|
external={true}
|
||||||
|
to='/forum'
|
||||||
|
>
|
||||||
|
Help Developers on the Forum
|
||||||
|
</Link>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Spacer size={4} />
|
||||||
|
</Grid>
|
||||||
|
</main>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
name: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
Welcome.propTypes = propTypes;
|
||||||
|
Welcome.displayName = 'Welcome';
|
||||||
|
|
||||||
|
export default Welcome;
|
14
client/src/components/welcome/welcome.css
Normal file
14
client/src/components/welcome/welcome.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.quote-partial .blockquote {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.quote-partial .blockquote {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.quote-author {
|
||||||
|
font-style: normal;
|
||||||
|
}
|
@ -32,7 +32,7 @@ const mapStateToProps = createSelector(
|
|||||||
);
|
);
|
||||||
const mapDispatchToProps = dispatch =>
|
const mapDispatchToProps = dispatch =>
|
||||||
bindActionCreators({ acceptTerms }, dispatch);
|
bindActionCreators({ acceptTerms }, dispatch);
|
||||||
const RedirectWelcome = createRedirect('/welcome');
|
const RedirectHome = createRedirect('/');
|
||||||
|
|
||||||
class AcceptPrivacyTerms extends Component {
|
class AcceptPrivacyTerms extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -66,7 +66,7 @@ class AcceptPrivacyTerms extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const { acceptedPrivacyTerms } = this.props;
|
const { acceptedPrivacyTerms } = this.props;
|
||||||
if (acceptedPrivacyTerms) {
|
if (acceptedPrivacyTerms) {
|
||||||
return <RedirectWelcome />;
|
return <RedirectHome />;
|
||||||
}
|
}
|
||||||
const { privacyPolicy, termsOfService, quincyEmail } = this.state;
|
const { privacyPolicy, termsOfService, quincyEmail } = this.state;
|
||||||
return (
|
return (
|
||||||
|
@ -11,8 +11,6 @@ import DonateForm from '../components/Donation/components/DonateForm';
|
|||||||
import DonateText from '../components/Donation/components/DonateText';
|
import DonateText from '../components/Donation/components/DonateText';
|
||||||
import PoweredByStripe from '../components/Donation/components/poweredByStripe';
|
import PoweredByStripe from '../components/Donation/components/poweredByStripe';
|
||||||
|
|
||||||
import './index.css';
|
|
||||||
|
|
||||||
class DonatePage extends Component {
|
class DonatePage extends Component {
|
||||||
constructor(...props) {
|
constructor(...props) {
|
||||||
super(...props);
|
super(...props);
|
||||||
|
@ -1,240 +1,71 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React from 'react';
|
||||||
import { Grid, Row, Col, Image } from '@freecodecamp/react-bootstrap';
|
import { createSelector } from 'reselect';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { connect } from 'react-redux';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { Loader } from '../components/helpers';
|
||||||
|
import Welcome from '../components/welcome';
|
||||||
|
import Landing from '../components/landing';
|
||||||
import {
|
import {
|
||||||
faHtml5,
|
userSelector,
|
||||||
faCss3Alt,
|
userFetchStateSelector,
|
||||||
faJs,
|
isSignedInSelector
|
||||||
faGithub,
|
} from '../redux';
|
||||||
faNodeJs,
|
import createRedirect from '../components/createRedirect';
|
||||||
faReact
|
|
||||||
} from '@fortawesome/free-brands-svg-icons';
|
|
||||||
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
|
||||||
import Helmet from 'react-helmet';
|
|
||||||
|
|
||||||
import { Link, Spacer } from '../components/helpers';
|
const mapStateToProps = createSelector(
|
||||||
import Login from '../components/Header/components/Login';
|
userFetchStateSelector,
|
||||||
|
isSignedInSelector,
|
||||||
import './index.css';
|
userSelector,
|
||||||
|
(fetchState, isSignedIn, user) => ({
|
||||||
const BigCallToAction = () => (
|
fetchState,
|
||||||
<Row>
|
isSignedIn,
|
||||||
<Col sm={8} smOffset={2} xs={12}>
|
user
|
||||||
<Login block={true}>Start coding (it's free)</Login>
|
})
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const IndexPage = () => (
|
const RedirectAcceptPrivacyTerm = createRedirect('/accept-privacy-terms');
|
||||||
<Fragment>
|
|
||||||
<Helmet>
|
|
||||||
<title>Learn to code | freeCodeCamp.org</title>
|
|
||||||
</Helmet>
|
|
||||||
<Spacer size={2} />
|
|
||||||
<Grid className='text-center'>
|
|
||||||
<Row>
|
|
||||||
<h1 className='big-heading'>Learn to code for free.</h1>
|
|
||||||
<Spacer />
|
|
||||||
<Col md={4} sm={12}>
|
|
||||||
<Image
|
|
||||||
alt={
|
|
||||||
'Get great references and connections to start your software ' +
|
|
||||||
'engineer career'
|
|
||||||
}
|
|
||||||
className='landing-icon img-center'
|
|
||||||
responsive={true}
|
|
||||||
src={
|
|
||||||
'https://s3.amazonaws.com/freecodecamp/landing-icon-community.svg'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<p className='large-p'>
|
|
||||||
Join a supportive community of millions of coders.
|
|
||||||
</p>
|
|
||||||
</Col>
|
|
||||||
<Col md={4} sm={12}>
|
|
||||||
<Image
|
|
||||||
alt='Help nonprofits with pro bono code projects'
|
|
||||||
className='landing-icon img-center'
|
|
||||||
responsive={true}
|
|
||||||
src={
|
|
||||||
'https://s3.amazonaws.com/freecodecamp/landing-icon-' +
|
|
||||||
'certificate.svg'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<p className='large-p'>
|
|
||||||
Build projects and earn free certifications.
|
|
||||||
</p>
|
|
||||||
</Col>
|
|
||||||
<Col md={4} sm={12}>
|
|
||||||
<Image
|
|
||||||
alt={
|
|
||||||
'Get hired as a developer and start your software engineer career'
|
|
||||||
}
|
|
||||||
className='landing-icon img-center'
|
|
||||||
responsive={true}
|
|
||||||
src={
|
|
||||||
'https://s3.amazonaws.com/freecodecamp/landing-icon-' +
|
|
||||||
'experience.svg'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<p className='large-p'>Get experience by coding for nonprofits.</p>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Spacer size={2} />
|
|
||||||
<BigCallToAction />
|
|
||||||
<Spacer />
|
|
||||||
<h2>As featured in:</h2>
|
|
||||||
<Image
|
|
||||||
alt='companies featuring freeCodeCamp'
|
|
||||||
className='img-center'
|
|
||||||
responsive={true}
|
|
||||||
src='https://s3.amazonaws.com/freecodecamp/as-seen-on.png'
|
|
||||||
/>
|
|
||||||
<Spacer />
|
|
||||||
<hr />
|
|
||||||
<Spacer />
|
|
||||||
<h2>Launch your developer career</h2>
|
|
||||||
<Spacer />
|
|
||||||
<Row>
|
|
||||||
<Col md={4} sm={12}>
|
|
||||||
<Image
|
|
||||||
alt="Meta's testimonial image"
|
|
||||||
className='testimonial-image img-center'
|
|
||||||
responsive={true}
|
|
||||||
src='https://cdn-media-1.freecodecamp.org/imgr/nsvNixW.jpg'
|
|
||||||
/>
|
|
||||||
<p className='testimonial-copy'>
|
|
||||||
Through freeCodeCamp, I built a robust and highly functional web app
|
|
||||||
for a nonprofit. This led me to getting a fantastic job.
|
|
||||||
</p>
|
|
||||||
<h3>- Meta Hirschl</h3>
|
|
||||||
</Col>
|
|
||||||
<Col md={4} sm={12}>
|
|
||||||
<Image
|
|
||||||
alt="Brian's testimonial image"
|
|
||||||
className='testimonial-image img-center'
|
|
||||||
responsive={true}
|
|
||||||
src='https://cdn-media-1.freecodecamp.org/imgr/QPpjPac.jpg'
|
|
||||||
/>
|
|
||||||
<p className='testimonial-copy'>
|
|
||||||
freeCodeCamp is a great way for disabled veterans like me to
|
|
||||||
retrain. I'm already receiving software engineering job offers.
|
|
||||||
</p>
|
|
||||||
<h3>- Brian Grant</h3>
|
|
||||||
</Col>
|
|
||||||
<Col md={4} sm={12}>
|
|
||||||
<Image
|
|
||||||
alt="Maxim Orlov's testimonial image"
|
|
||||||
className='testimonial-image img-center'
|
|
||||||
responsive={true}
|
|
||||||
src='https://cdn-media-1.freecodecamp.org/imgr/wjlDigg.jpg'
|
|
||||||
/>
|
|
||||||
<p className='testimonial-copy'>
|
|
||||||
I joined freeCodeCamp with zero knowledge of web development. 6
|
|
||||||
months later, I landed my first job as a back end engineer.
|
|
||||||
</p>
|
|
||||||
<h3>- Maxim Orlov</h3>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Spacer />
|
|
||||||
<hr />
|
|
||||||
<Spacer />
|
|
||||||
<h2>Learn powerful skills</h2>
|
|
||||||
<Spacer />
|
|
||||||
<Row className='text-center'>
|
|
||||||
<Col md={3} sm={6} xs={12}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
className='landing-skill-icon'
|
|
||||||
icon={faHtml5}
|
|
||||||
size='9x'
|
|
||||||
/>
|
|
||||||
<h2 className='black-text'>HTML5</h2>
|
|
||||||
</Col>
|
|
||||||
<Col md={3} sm={6} xs={12}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
className='landing-skill-icon'
|
|
||||||
icon={faCss3Alt}
|
|
||||||
size='9x'
|
|
||||||
/>
|
|
||||||
<h2 className='black-text'>CSS3</h2>
|
|
||||||
</Col>
|
|
||||||
<Col md={3} sm={6} xs={12}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
className='landing-skill-icon'
|
|
||||||
icon={faJs}
|
|
||||||
size='9x'
|
|
||||||
/>
|
|
||||||
<h2 className='black-text'>JavaScript</h2>
|
|
||||||
</Col>
|
|
||||||
<Col md={3} sm={6} xs={12}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
className='landing-skill-icon'
|
|
||||||
icon={faDatabase}
|
|
||||||
size='9x'
|
|
||||||
/>
|
|
||||||
<h2 className='black-text'>Databases</h2>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row className='text-center'>
|
|
||||||
<Col md={3} sm={6} xs={12}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
className='landing-skill-icon'
|
|
||||||
icon={faGithub}
|
|
||||||
size='9x'
|
|
||||||
/>
|
|
||||||
<h2 className='black-text'>Git & GitHub</h2>
|
|
||||||
</Col>
|
|
||||||
<Col md={3} sm={6} xs={12}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
className='landing-skill-icon'
|
|
||||||
icon={faNodeJs}
|
|
||||||
size='9x'
|
|
||||||
/>
|
|
||||||
<h2 className='black-text'>Node.js</h2>
|
|
||||||
</Col>
|
|
||||||
<Col md={3} sm={6} xs={12}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
className='landing-skill-icon'
|
|
||||||
icon={faReact}
|
|
||||||
size='9x'
|
|
||||||
/>
|
|
||||||
<h2 className='black-text'>React.js</h2>
|
|
||||||
</Col>
|
|
||||||
<Col md={3} sm={6} xs={12}>
|
|
||||||
<Image
|
|
||||||
alt='The D3.js Logo'
|
|
||||||
className='landing-skill-icon custom-landing-skill-icon'
|
|
||||||
src='https://s3.amazonaws.com/freecodecamp/d3-logo.svg'
|
|
||||||
/>
|
|
||||||
<h2 className='black-text'>D3.js</h2>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<hr />
|
|
||||||
<Spacer />
|
|
||||||
<h3>
|
|
||||||
freeCodeCamp is a donor-supported tax-exempt 501(c)(3) nonprofit
|
|
||||||
organization (United States Federal Tax Identification Number:
|
|
||||||
82-0779546)
|
|
||||||
</h3>
|
|
||||||
<p className='large-p'>
|
|
||||||
Our mission: to help people learn to code for free. We accomplish this
|
|
||||||
by creating thousands of videos, articles, and interactive coding
|
|
||||||
lessons - all freely available to the public. We also have thousands of
|
|
||||||
freeCodeCamp study groups around the world.
|
|
||||||
</p>
|
|
||||||
<p className='large-p'>
|
|
||||||
Donations to freeCodeCamp go toward our education initiatives, and help
|
|
||||||
pay for servers, services, and staff. You can{' '}
|
|
||||||
<Link className='large-p underlined-link' external={true} to='/donate'>
|
|
||||||
make a tax-deductible donation here
|
|
||||||
</Link>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
<Spacer />
|
|
||||||
<BigCallToAction />
|
|
||||||
<Spacer size={2} />
|
|
||||||
</Grid>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default IndexPage;
|
export const IndexPage = ({
|
||||||
|
fetchState: { pending, complete },
|
||||||
|
isSignedIn,
|
||||||
|
user: { acceptedPrivacyTerms, name = '' }
|
||||||
|
}) => {
|
||||||
|
if (pending && !complete) {
|
||||||
|
return <Loader fullScreen={true} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSignedIn && !acceptedPrivacyTerms) {
|
||||||
|
return <RedirectAcceptPrivacyTerm />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSignedIn) {
|
||||||
|
return <Welcome name={name} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Landing />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
fetchState: PropTypes.shape({
|
||||||
|
pending: PropTypes.bool,
|
||||||
|
complete: PropTypes.bool,
|
||||||
|
errored: PropTypes.bool
|
||||||
|
}),
|
||||||
|
isSignedIn: PropTypes.bool,
|
||||||
|
user: PropTypes.shape({
|
||||||
|
acceptedPrivacyTerms: PropTypes.bool,
|
||||||
|
completedCertCount: PropTypes.number,
|
||||||
|
completedChallengeCount: PropTypes.number,
|
||||||
|
completedLegacyCertCount: PropTypes.number,
|
||||||
|
completedProjectCount: PropTypes.number,
|
||||||
|
isDonating: PropTypes.bool,
|
||||||
|
name: PropTypes.string,
|
||||||
|
username: PropTypes.string
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
IndexPage.propTypes = propTypes;
|
||||||
|
IndexPage.displayName = 'IndexPage';
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(IndexPage);
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
.quote-partial .blockquote {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quote-author {
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.update-link {
|
|
||||||
text-decoration: underline;
|
|
||||||
font-size: 1.4em;
|
|
||||||
color: var(--secondary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.stats {
|
|
||||||
font-size: 1.45rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.stats span {
|
|
||||||
color: var(--secondary-color);
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import { Grid, Row, Col } from '@freecodecamp/react-bootstrap';
|
|
||||||
import Helmet from 'react-helmet';
|
|
||||||
|
|
||||||
import { CurrentChallengeLink, Loader, Spacer } from '../components/helpers';
|
|
||||||
import Supporters from '../components/Supporters';
|
|
||||||
import {
|
|
||||||
userSelector,
|
|
||||||
userFetchStateSelector,
|
|
||||||
isSignedInSelector,
|
|
||||||
activeDonationsSelector
|
|
||||||
} from '../redux';
|
|
||||||
import { randomQuote } from '../utils/get-words';
|
|
||||||
import createRedirect from '../components/createRedirect';
|
|
||||||
import RedirectHome from '../components/RedirectHome';
|
|
||||||
|
|
||||||
import './welcome.css';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
activeDonations: PropTypes.number,
|
|
||||||
fetchState: PropTypes.shape({
|
|
||||||
pending: PropTypes.bool,
|
|
||||||
complete: PropTypes.bool,
|
|
||||||
errored: PropTypes.bool
|
|
||||||
}),
|
|
||||||
isSignedIn: PropTypes.bool,
|
|
||||||
user: PropTypes.shape({
|
|
||||||
acceptedPrivacyTerms: PropTypes.bool,
|
|
||||||
completedCertCount: PropTypes.number,
|
|
||||||
completedChallengeCount: PropTypes.number,
|
|
||||||
completedLegacyCertCount: PropTypes.number,
|
|
||||||
completedProjectCount: PropTypes.number,
|
|
||||||
isDonating: PropTypes.bool,
|
|
||||||
name: PropTypes.string,
|
|
||||||
username: PropTypes.string
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
|
||||||
userFetchStateSelector,
|
|
||||||
isSignedInSelector,
|
|
||||||
userSelector,
|
|
||||||
activeDonationsSelector,
|
|
||||||
(fetchState, isSignedIn, user, activeDonations) => ({
|
|
||||||
activeDonations,
|
|
||||||
fetchState,
|
|
||||||
isSignedIn,
|
|
||||||
user
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);
|
|
||||||
const RedirectAcceptPrivacyTerm = createRedirect('/accept-privacy-terms');
|
|
||||||
|
|
||||||
function Welcome({
|
|
||||||
fetchState: { pending, complete },
|
|
||||||
isSignedIn,
|
|
||||||
user: {
|
|
||||||
acceptedPrivacyTerms,
|
|
||||||
name = '',
|
|
||||||
completedChallengeCount: completedChallenges = 0,
|
|
||||||
completedProjectCount = 0,
|
|
||||||
completedCertCount = 0,
|
|
||||||
completedLegacyCertCount: completedLegacyCerts = 0,
|
|
||||||
isDonating
|
|
||||||
},
|
|
||||||
activeDonations
|
|
||||||
}) {
|
|
||||||
if (pending && !complete) {
|
|
||||||
return <Loader fullScreen={true} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSignedIn) {
|
|
||||||
return <RedirectHome />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSignedIn && !acceptedPrivacyTerms) {
|
|
||||||
return <RedirectAcceptPrivacyTerm />;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { quote, author } = randomQuote();
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<Helmet>
|
|
||||||
<title>Welcome | freeCodeCamp.org</title>
|
|
||||||
</Helmet>
|
|
||||||
<main>
|
|
||||||
<Grid>
|
|
||||||
<Row>
|
|
||||||
<Col xs={12}>
|
|
||||||
<Spacer />
|
|
||||||
<h1 className='big-heading text-center'>
|
|
||||||
Welcome {name ? name : 'Camper'}!
|
|
||||||
</h1>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Spacer />
|
|
||||||
<Supporters
|
|
||||||
activeDonations={activeDonations}
|
|
||||||
isDonating={isDonating}
|
|
||||||
/>
|
|
||||||
<Spacer size={2} />
|
|
||||||
<Spacer />
|
|
||||||
<Row className='quote-partial'>
|
|
||||||
<Col sm={10} smOffset={1} xs={12}>
|
|
||||||
<blockquote className='blockquote'>
|
|
||||||
<span>
|
|
||||||
<q>{quote}</q>
|
|
||||||
<footer className='quote-author blockquote-footer'>
|
|
||||||
<cite>{author}</cite>
|
|
||||||
</footer>
|
|
||||||
</span>
|
|
||||||
</blockquote>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Spacer />
|
|
||||||
<Row>
|
|
||||||
<Col sm={8} smOffset={2} xs={12}>
|
|
||||||
<p className='stats'>
|
|
||||||
You have completed <span>{completedChallenges}</span> of{' '}
|
|
||||||
<span>1408</span> coding challenges.
|
|
||||||
</p>
|
|
||||||
<p className='stats'>
|
|
||||||
You have built <span>{completedProjectCount}</span> of{' '}
|
|
||||||
<span>30</span> projects.
|
|
||||||
</p>
|
|
||||||
{completedLegacyCerts ? (
|
|
||||||
<p className='stats'>
|
|
||||||
You have earned <span>{completedLegacyCerts}</span> of{' '}
|
|
||||||
<span>4</span> legacy certifications.
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
<p className='stats'>
|
|
||||||
You have earned <span>{completedCertCount}</span> of{' '}
|
|
||||||
<span>6</span> certifications.
|
|
||||||
</p>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Spacer />
|
|
||||||
<Row>
|
|
||||||
<Col sm={6} smOffset={3} xs={12}>
|
|
||||||
<CurrentChallengeLink>
|
|
||||||
Go to my next challenge
|
|
||||||
</CurrentChallengeLink>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Spacer size={4} />
|
|
||||||
</Grid>
|
|
||||||
</main>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Welcome.displayName = 'Welcome';
|
|
||||||
Welcome.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(Welcome);
|
|
@ -18,7 +18,7 @@ function* acceptTermsSaga({ payload: quincyEmails }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function* acceptCompleteSaga() {
|
function* acceptCompleteSaga() {
|
||||||
yield call(navigate, '/welcome');
|
yield call(navigate, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createAcceptTermsSaga(types) {
|
export function createAcceptTermsSaga(types) {
|
||||||
|
@ -37,7 +37,7 @@ function* resetProgressSaga() {
|
|||||||
// wait for the refresh to complete
|
// wait for the refresh to complete
|
||||||
yield take(appTypes.fetchUserComplete);
|
yield take(appTypes.fetchUserComplete);
|
||||||
// the complete action has been called
|
// the complete action has been called
|
||||||
yield call(navigate, '/welcome');
|
yield call(navigate, '/');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
yield put(resetProgressError(e));
|
yield put(resetProgressError(e));
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export function wrapHandledError(err, { type, message, redirectTo }) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handle400Error(e, options = { redirectTo: '/welcome' }) {
|
export function handle400Error(e, options = { redirectTo: '/' }) {
|
||||||
const {
|
const {
|
||||||
response: { status }
|
response: { status }
|
||||||
} = e;
|
} = e;
|
||||||
@ -54,7 +54,7 @@ export function handle400Error(e, options = { redirectTo: '/welcome' }) {
|
|||||||
export function handle500Error(
|
export function handle500Error(
|
||||||
e,
|
e,
|
||||||
options = {
|
options = {
|
||||||
redirectTo: '/welcome'
|
redirectTo: '/'
|
||||||
},
|
},
|
||||||
_reportClientSideError = reportClientSideError
|
_reportClientSideError = reportClientSideError
|
||||||
) {
|
) {
|
||||||
|
@ -11,13 +11,6 @@ export default function layoutSelector({ element, props }) {
|
|||||||
const {
|
const {
|
||||||
location: { pathname }
|
location: { pathname }
|
||||||
} = props;
|
} = props;
|
||||||
if (pathname === '/') {
|
|
||||||
return (
|
|
||||||
<DefaultLayout landingPage={true} pathname={pathname}>
|
|
||||||
{element}
|
|
||||||
</DefaultLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (element.type === FourOhFourPage) {
|
if (element.type === FourOhFourPage) {
|
||||||
return <DefaultLayout pathname={pathname}>{element}</DefaultLayout>;
|
return <DefaultLayout pathname={pathname}>{element}</DefaultLayout>;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user