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 => {
|
||||
if (!!req && req.session && req.session.returnTo) {
|
||||
delete req.session.returnTo;
|
||||
return `${homeLocation}/welcome`;
|
||||
return `${homeLocation}/`;
|
||||
}
|
||||
return `${homeLocation}/welcome`;
|
||||
return `${homeLocation}/`;
|
||||
};
|
||||
|
||||
let redirect = url.parse(successRedirect(req), true);
|
||||
@ -112,7 +112,7 @@ export const createPassportCallbackAuthenticator = (strategy, config) => (
|
||||
if (!user || !userInfo) {
|
||||
return res.redirect('/signin');
|
||||
}
|
||||
const redirect = `${homeLocation}/welcome`;
|
||||
const redirect = `${homeLocation}/`;
|
||||
|
||||
const { accessToken } = userInfo;
|
||||
const { provider } = config;
|
||||
|
@ -3,7 +3,7 @@ import { homeLocation, apiLocation } from '../../config/env';
|
||||
|
||||
const { clientID, clientSecret, domain } = auth0;
|
||||
|
||||
const successRedirect = `${homeLocation}/welcome`;
|
||||
const successRedirect = `${homeLocation}/`;
|
||||
const failureRedirect = `${homeLocation}/signin`;
|
||||
|
||||
export default {
|
||||
|
@ -55,7 +55,7 @@ export function ifNotVerifiedRedirectToUpdateEmail(req, res, next) {
|
||||
return next();
|
||||
}
|
||||
|
||||
export function ifUserRedirectTo(path = `${homeLocation}/welcome`, status) {
|
||||
export function ifUserRedirectTo(path = `${homeLocation}/`, status) {
|
||||
status = status === 302 ? 302 : 301;
|
||||
return (req, res, next) => {
|
||||
const { accessToken } = getAccessTokenFromRequest(req);
|
||||
|
@ -54,7 +54,6 @@
|
||||
}
|
||||
|
||||
.donation-email-container input {
|
||||
color: var(--secondary-background);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ const createOnClick = (navigate, isSignedIn) => e => {
|
||||
e.preventDefault();
|
||||
gtagReportConversion();
|
||||
if (isSignedIn) {
|
||||
return gatsbyNavigate('/welcome');
|
||||
return gatsbyNavigate('/');
|
||||
}
|
||||
return navigate(`${apiLocation}/signin`);
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ function Header(props) {
|
||||
<Link className='home-link' to='/'>
|
||||
<NavLogo />
|
||||
</Link>
|
||||
{disableSettings ? null : <SearchBar />}
|
||||
<SearchBar />
|
||||
<NavigationMenu disableSettings={disableSettings} />
|
||||
</nav>
|
||||
</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 './global.css';
|
||||
import './layout.css';
|
||||
import './variables.css';
|
||||
|
||||
fontawesome.config = {
|
||||
@ -67,7 +66,6 @@ const propTypes = {
|
||||
hasMessage: PropTypes.bool,
|
||||
isOnline: PropTypes.bool.isRequired,
|
||||
isSignedIn: PropTypes.bool,
|
||||
landingPage: PropTypes.bool,
|
||||
onlineStatusChange: PropTypes.func.isRequired,
|
||||
pathname: PropTypes.string.isRequired,
|
||||
removeFlashMessage: PropTypes.func.isRequired,
|
||||
@ -133,7 +131,6 @@ class DefaultLayout extends Component {
|
||||
flashMessage,
|
||||
isOnline,
|
||||
isSignedIn,
|
||||
landingPage,
|
||||
removeFlashMessage,
|
||||
showFooter = true,
|
||||
theme
|
||||
@ -157,11 +154,8 @@ class DefaultLayout extends Component {
|
||||
<style>{fontawesome.dom.css()}</style>
|
||||
</Helmet>
|
||||
<WithInstantSearch>
|
||||
<Header disableSettings={landingPage} />
|
||||
<div
|
||||
className={`default-layout
|
||||
${landingPage ? 'landing-page' : ''}`}
|
||||
>
|
||||
<Header disableSettings={!isSignedIn} />
|
||||
<div className={`default-layout`}>
|
||||
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
||||
{hasMessage && flashMessage ? (
|
||||
<Flash flashMessage={flashMessage} onClose={removeFlashMessage} />
|
||||
|
@ -14,10 +14,6 @@ body {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.big-heading {
|
||||
font-size: 50px !important;
|
||||
}
|
||||
|
||||
.default-layout {
|
||||
margin-top: var(--header-height);
|
||||
background: var(--secondary-background);
|
||||
@ -62,6 +58,33 @@ th {
|
||||
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-align: center !important;
|
||||
}
|
||||
@ -351,3 +374,9 @@ pre {
|
||||
.nav-tabs > li > a {
|
||||
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 =>
|
||||
bindActionCreators({ acceptTerms }, dispatch);
|
||||
const RedirectWelcome = createRedirect('/welcome');
|
||||
const RedirectHome = createRedirect('/');
|
||||
|
||||
class AcceptPrivacyTerms extends Component {
|
||||
constructor(props) {
|
||||
@ -66,7 +66,7 @@ class AcceptPrivacyTerms extends Component {
|
||||
render() {
|
||||
const { acceptedPrivacyTerms } = this.props;
|
||||
if (acceptedPrivacyTerms) {
|
||||
return <RedirectWelcome />;
|
||||
return <RedirectHome />;
|
||||
}
|
||||
const { privacyPolicy, termsOfService, quincyEmail } = this.state;
|
||||
return (
|
||||
|
@ -11,8 +11,6 @@ import DonateForm from '../components/Donation/components/DonateForm';
|
||||
import DonateText from '../components/Donation/components/DonateText';
|
||||
import PoweredByStripe from '../components/Donation/components/poweredByStripe';
|
||||
|
||||
import './index.css';
|
||||
|
||||
class DonatePage extends Component {
|
||||
constructor(...props) {
|
||||
super(...props);
|
||||
|
@ -1,240 +1,71 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import { Grid, Row, Col, Image } from '@freecodecamp/react-bootstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React from 'react';
|
||||
import { createSelector } from 'reselect';
|
||||
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 {
|
||||
faHtml5,
|
||||
faCss3Alt,
|
||||
faJs,
|
||||
faGithub,
|
||||
faNodeJs,
|
||||
faReact
|
||||
} from '@fortawesome/free-brands-svg-icons';
|
||||
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
|
||||
import Helmet from 'react-helmet';
|
||||
userSelector,
|
||||
userFetchStateSelector,
|
||||
isSignedInSelector
|
||||
} from '../redux';
|
||||
import createRedirect from '../components/createRedirect';
|
||||
|
||||
import { Link, Spacer } from '../components/helpers';
|
||||
import Login from '../components/Header/components/Login';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const BigCallToAction = () => (
|
||||
<Row>
|
||||
<Col sm={8} smOffset={2} xs={12}>
|
||||
<Login block={true}>Start coding (it's free)</Login>
|
||||
</Col>
|
||||
</Row>
|
||||
const mapStateToProps = createSelector(
|
||||
userFetchStateSelector,
|
||||
isSignedInSelector,
|
||||
userSelector,
|
||||
(fetchState, isSignedIn, user) => ({
|
||||
fetchState,
|
||||
isSignedIn,
|
||||
user
|
||||
})
|
||||
);
|
||||
|
||||
const IndexPage = () => (
|
||||
<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>
|
||||
);
|
||||
const RedirectAcceptPrivacyTerm = createRedirect('/accept-privacy-terms');
|
||||
|
||||
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() {
|
||||
yield call(navigate, '/welcome');
|
||||
yield call(navigate, '/');
|
||||
}
|
||||
|
||||
export function createAcceptTermsSaga(types) {
|
||||
|
@ -37,7 +37,7 @@ function* resetProgressSaga() {
|
||||
// wait for the refresh to complete
|
||||
yield take(appTypes.fetchUserComplete);
|
||||
// the complete action has been called
|
||||
yield call(navigate, '/welcome');
|
||||
yield call(navigate, '/');
|
||||
} catch (e) {
|
||||
yield put(resetProgressError(e));
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export function wrapHandledError(err, { type, message, redirectTo }) {
|
||||
return err;
|
||||
}
|
||||
|
||||
export function handle400Error(e, options = { redirectTo: '/welcome' }) {
|
||||
export function handle400Error(e, options = { redirectTo: '/' }) {
|
||||
const {
|
||||
response: { status }
|
||||
} = e;
|
||||
@ -54,7 +54,7 @@ export function handle400Error(e, options = { redirectTo: '/welcome' }) {
|
||||
export function handle500Error(
|
||||
e,
|
||||
options = {
|
||||
redirectTo: '/welcome'
|
||||
redirectTo: '/'
|
||||
},
|
||||
_reportClientSideError = reportClientSideError
|
||||
) {
|
||||
|
@ -11,13 +11,6 @@ export default function layoutSelector({ element, props }) {
|
||||
const {
|
||||
location: { pathname }
|
||||
} = props;
|
||||
if (pathname === '/') {
|
||||
return (
|
||||
<DefaultLayout landingPage={true} pathname={pathname}>
|
||||
{element}
|
||||
</DefaultLayout>
|
||||
);
|
||||
}
|
||||
if (element.type === FourOhFourPage) {
|
||||
return <DefaultLayout pathname={pathname}>{element}</DefaultLayout>;
|
||||
}
|
||||
|
Reference in New Issue
Block a user