feat: update donation plans on donate page. (#40102)

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Ahmad Abdolsaheb
2020-11-06 14:30:14 +03:00
committed by GitHub
parent 7ce85a8f03
commit ca369b8585
10 changed files with 218 additions and 195 deletions

View File

@ -12,9 +12,6 @@ import {
ToggleButton, ToggleButton,
ToggleButtonGroup ToggleButtonGroup
} from '@freecodecamp/react-bootstrap'; } from '@freecodecamp/react-bootstrap';
import ApplePay from './assets/ApplePay';
import GooglePay from './assets/GooglePay';
import acceptedCards from './assets/accepted-cards.png';
import { import {
amountsConfig, amountsConfig,
durationsConfig, durationsConfig,
@ -241,7 +238,7 @@ class DonateForm extends Component {
const { donationAmount, donationDuration, processing } = this.state; const { donationAmount, donationDuration, processing } = this.state;
return !processing ? ( return !processing ? (
<div> <div>
<h3>Duration and amount:</h3> <h3>Select gift frequency:</h3>
<Tabs <Tabs
activeKey={donationDuration} activeKey={donationDuration}
animation={false} animation={false}
@ -257,6 +254,7 @@ class DonateForm extends Component {
title={this.durations[duration]} title={this.durations[duration]}
> >
<Spacer /> <Spacer />
<h3>Select gift amount:</h3>
<div> <div>
<ToggleButtonGroup <ToggleButtonGroup
animation={`false`} animation={`false`}
@ -306,45 +304,15 @@ class DonateForm extends Component {
</b> </b>
)} )}
<Spacer /> <Spacer />
<div className='donate-btn-group'>
<Button <Button
block={true} block={true}
bsStyle='primary' bsStyle='primary'
className='btn-cta'
id='confirm-donation-btn'
onClick={e => this.handleStripeCheckoutRedirect(e, 'apple pay')}
>
<span>Donate with Apple Pay</span>
<ApplePay className='apple-pay-logo' />
</Button>
<Spacer />
<Button
block={true}
bsStyle='primary'
className='btn-cta'
id='confirm-donation-btn'
onClick={e => this.handleStripeCheckoutRedirect(e, 'google pay')}
>
<span>Donate with Google Pay</span>
<GooglePay className='google-pay-logo' />
</Button>
<Spacer />
<Button
block={true}
bsStyle='primary'
className='btn-cta'
id='confirm-donation-btn' id='confirm-donation-btn'
onClick={e => this.handleStripeCheckoutRedirect(e, 'credit card')} onClick={e => this.handleStripeCheckoutRedirect(e, 'credit card')}
> >
<span>Donate with Card</span> <b>Credit Card</b>
<img
alt='accepted cards'
className='accepted-cards'
src={acceptedCards}
/>
</Button> </Button>
<Spacer />
<PaypalButton <PaypalButton
addDonation={addDonation} addDonation={addDonation}
donationAmount={donationAmount} donationAmount={donationAmount}
@ -354,7 +322,7 @@ class DonateForm extends Component {
onDonationStateChange={this.onDonationStateChange} onDonationStateChange={this.onDonationStateChange}
skipAddDonation={!isSignedIn} skipAddDonation={!isSignedIn}
/> />
<Spacer size={2} /> </div>
</div> </div>
); );
} }
@ -415,12 +383,8 @@ class DonateForm extends Component {
renderPageForm() { renderPageForm() {
return ( return (
<Row> <Row>
<Col sm={10} smOffset={1} xs={12}> <Col xs={12}>{this.renderDurationAmountOptions()}</Col>
{this.renderDurationAmountOptions()} <Col xs={12}>{this.renderDonationOptions()}</Col>
</Col>
<Col sm={10} smOffset={1} xs={12}>
{this.renderDonationOptions()}
</Col>
</Row> </Row>
); );
} }

View File

@ -0,0 +1,19 @@
import React from 'react';
import { Row, Col } from '@freecodecamp/react-bootstrap';
const DonateSupportText = () => (
<Row className='donate-text'>
<Col xs={12}>
<hr />
<h4>
<b>Need help with your current or past donations?</b>
</h4>
<p>
Forward a copy of your donation receipt to donors@freecodecamp.org and
tell us how we can help.
</p>
</Col>
</Row>
);
DonateSupportText.displayName = 'DonateText';
export default DonateSupportText;

View File

@ -4,17 +4,8 @@ import { Row, Col } from '@freecodecamp/react-bootstrap';
const DonateText = () => { const DonateText = () => {
return ( return (
<Row className='donate-text'> <Row className='donate-text'>
<Col sm={10} smOffset={1} xs={12}> <Col xs={12}>
<p>freeCodeCamp is a highly efficient education nonprofit.</p> <p>freeCodeCamp is a highly efficient education nonprofit.</p>
<p>
In 2019 alone, we provided 18 million hours of free education to
people around the world.
</p>
<p>
Since freeCodeCamp's total budget is only $373,000, that means every
dollar you donate to freeCodeCamp translates into 50 hours worth of
technology education.
</p>
<p> <p>
When you donate to freeCodeCamp, you help people learn new skills and When you donate to freeCodeCamp, you help people learn new skills and
provide for their families. provide for their families.
@ -23,14 +14,6 @@ const DonateText = () => {
You also help us create new resources for you to use to expand your You also help us create new resources for you to use to expand your
own technology skills. own technology skills.
</p> </p>
<hr />
<h4>
<b>Need help with your current or past donations?</b>
</h4>
<p>
Forward a copy of your donation receipt to donors@freecodecamp.org and
tell us how we can help.
</p>
</Col> </Col>
</Row> </Row>
); );

View File

@ -92,7 +92,7 @@
.donate-tabs > .nav-pills > li > a { .donate-tabs > .nav-pills > li > a {
text-transform: capitalize; text-transform: capitalize;
text-decoration: none; text-decoration: none;
border: 2px solid var(--yellow-light); border: 3px solid var(--yellow-light);
border-radius: 0px; border-radius: 0px;
color: var(--gray-85); color: var(--gray-85);
margin: 0 1px; margin: 0 1px;
@ -109,7 +109,7 @@
.donate-tabs > .nav-pills > li.active > a:focus { .donate-tabs > .nav-pills > li.active > a:focus {
color: var(--gray-85); color: var(--gray-85);
background-color: var(--yellow-light); background-color: var(--yellow-light);
border: 2px solid var(--yellow-light); border: 3px solid var(--yellow-light);
text-decoration: none; text-decoration: none;
border-radius: 0px; border-radius: 0px;
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
@ -125,13 +125,17 @@
} }
.amount-values > label { .amount-values > label {
margin: 0 2px !important; margin: 0 2px;
color: var(--gray-85); color: var(--gray-85);
border: 2px solid var(--yellow-light); border: 3px solid var(--yellow-light);
border-radius: 0px; border-radius: 0px;
background-color: transparent; background-color: transparent;
} }
.amount-values.btn-group .btn + .btn {
margin: 0 2px;
}
.amount-values > label:hover, .amount-values > label:hover,
.amount-values > label:focus, .amount-values > label:focus,
.amount-values > label:active:hover { .amount-values > label:active:hover {
@ -155,17 +159,36 @@
.amount-values > label.active:focus { .amount-values > label.active:focus {
color: var(--gray-85); color: var(--gray-85);
background-color: var(--yellow-light); background-color: var(--yellow-light);
border: 2px solid var(--yellow-light); border: 3px solid var(--yellow-light);
border-radius: 0px; border-radius: 0px;
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
} }
@media (max-width: 500px) {
.amount-values {
display: flex;
flex-wrap: wrap;
}
.amount-values > label {
width: 31.3%;
margin-bottom: 3px;
}
.amount-values.btn-group > .btn:first-child {
margin-left: 2px;
margin-bottom: 3px;
}
.amount-values.btn-group .btn + .btn {
/* margin: 0 2px; */
margin-bottom: 3px;
}
}
li.disabled { li.disabled {
cursor: not-allowed; cursor: not-allowed;
} }
li.disabled > a { li.disabled > a {
border: 2px solid var(--gray-15) !important; border: 3px solid var(--gray-15) !important;
color: var(--gray-15) !important; color: var(--gray-15) !important;
} }
@ -178,12 +201,6 @@ li.disabled > a {
font-family: 'Lato', sans-serif; font-family: 'Lato', sans-serif;
} }
@media (max-width: 991px) {
.donate-text {
margin-top: 30px;
}
}
@media (max-width: 400px) { @media (max-width: 400px) {
.donate-tabs > .nav-pills > li > a { .donate-tabs > .nav-pills > li > a {
font-size: 0.8rem; font-size: 0.8rem;
@ -342,13 +359,51 @@ li.disabled > a {
align-self: center; align-self: center;
} }
#confirm-donation-btn { button#confirm-donation-btn {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
align-content: center; align-content: center;
border-radius: 5px;
background-color: var(--yellow-light);
border-color: var(--yellow-light);
}
button#confirm-donation-btn:active,
button#confirm-donation-btn:active:focus,
button#confirm-donation-btn:hover {
color: var(--secondary-color);
background-color: #f2ba38;
border-color: #f2ba38;
} }
.hide { .hide {
display: none; display: none;
} }
.donate-btn-group {
display: flex;
flex-direction: column;
}
.donate-btn-group > * {
width: 100%;
height: 43px;
}
.donate-btn-group button:first-child {
margin-bottom: 10px;
}
@media (min-width: 500px) {
.donate-btn-group {
flex-direction: row;
}
.donate-btn-group > * {
width: 49%;
}
.donate-btn-group button:first-child {
margin-bottom: 0px;
margin-right: auto;
}
}

View File

@ -34,7 +34,7 @@ export class PayPalButtonScriptLoader extends Component {
loadScript(subscription, deleteScript) { loadScript(subscription, deleteScript) {
if (deleteScript) scriptRemover('paypal-sdk'); if (deleteScript) scriptRemover('paypal-sdk');
let queries = `?client-id=${this.props.clientId}&disable-funding=credit,card`; let queries = `?client-id=${this.props.clientId}&disable-funding=credit,card,bancontact,blik,eps,giropay,ideal,mybank,p24,sepa,sofort,venmo`;
if (subscription) queries += '&vault=true'; if (subscription) queries += '&vault=true';
scriptLoader( scriptLoader(

View File

@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import Media from 'react-responsive';
import { Spacer, ImageLoader } from '../../helpers';
import wideImg from '../../../assets/images/landing/wide-image.png';
const propTypes = {
page: PropTypes.string
};
const LARGE_SCREEN_SIZE = 1200;
const imageConfig = {
donate: {
spacerSize: 0,
height: 345,
width: 585
},
landing: {
spacerSize: 2,
height: 442,
width: 750
}
};
function CampersImage({ page }) {
const { spacerSize, height, width } = imageConfig[page];
return (
<Media minWidth={LARGE_SCREEN_SIZE}>
<Spacer size={spacerSize} />
<ImageLoader
alt='freeCodeCamp students at a local study group in South Korea.'
className='landing-page-image'
height={height}
src={wideImg}
width={width}
/>
<p className='text-center caption'>
freeCodeCamp students at a local study group in South Korea.
</p>
</Media>
);
}
CampersImage.displayName = 'CampersImage';
CampersImage.propTypes = propTypes;
export default CampersImage;

View File

@ -1,9 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Media from 'react-responsive';
import { Col, Row } from '@freecodecamp/react-bootstrap'; import { Col, Row } from '@freecodecamp/react-bootstrap';
import { Spacer, ImageLoader } from '../../helpers'; import { Spacer } from '../../helpers';
import wideImg from '../../../assets/images/landing/wide-image.png';
import Login from '../../Header/components/Login'; import Login from '../../Header/components/Login';
import { import {
AmazonLogo, AmazonLogo,
@ -12,29 +10,13 @@ import {
SpotifyLogo, SpotifyLogo,
GoogleLogo GoogleLogo
} from '../../../assets/images/components'; } from '../../../assets/images/components';
import CampersImage from './CampersImage';
const propTypes = { const propTypes = {
page: PropTypes.string page: PropTypes.string
}; };
const LARGE_SCREEN_SIZE = 1200;
function landingTop({ page }) { function landingTop({ page }) {
const landingImageSection = (
<Media minWidth={LARGE_SCREEN_SIZE}>
<Spacer size={2} />
<ImageLoader
alt='Freecodecamp students at a local study'
className='landing-page-image'
height={442}
src={wideImg}
width={750}
/>
<p className='text-center caption'>
freeCodeCamp students at a local study group in South Korea.
</p>
</Media>
);
const BigCallToAction = ( const BigCallToAction = (
<Login block={true} data-test-label={`${page}-big-cta`}> <Login block={true} data-test-label={`${page}-big-cta`}>
{page === 'landing' {page === 'landing'
@ -65,7 +47,7 @@ function landingTop({ page }) {
</div> </div>
<Spacer /> <Spacer />
{BigCallToAction} {BigCallToAction}
{landingImageSection} <CampersImage page={page} />
<Spacer /> <Spacer />
</Col> </Col>
</Row> </Row>

View File

@ -12,7 +12,7 @@
--gray-90: #0a0a23; --gray-90: #0a0a23;
--purple-light: #dbb8ff; --purple-light: #dbb8ff;
--purple-dark: #5a01a7; --purple-dark: #5a01a7;
--yellow-light: #f1be32; --yellow-light: #ffc300;
--yellow-dark: #4d3800; --yellow-dark: #4d3800;
--blue-light: #99c9ff; --blue-light: #99c9ff;
--blue-dark: #002ead; --blue-dark: #002ead;

View File

@ -9,7 +9,9 @@ import { Grid, Row, Col, Alert } from '@freecodecamp/react-bootstrap';
import { Spacer, Loader } from '../components/helpers'; import { Spacer, Loader } from '../components/helpers';
import DonateForm from '../components/Donation/DonateForm'; import DonateForm from '../components/Donation/DonateForm';
import DonateText from '../components/Donation/DonateText'; import DonateText from '../components/Donation/DonateText';
import DonateSupportText from '../components/Donation/DonateSupportText';
import { signInLoadingSelector, userSelector, executeGA } from '../redux'; import { signInLoadingSelector, userSelector, executeGA } from '../redux';
import CampersImage from '../components/landing/components/CampersImage';
const propTypes = { const propTypes = {
executeGA: PropTypes.func, executeGA: PropTypes.func,
@ -79,20 +81,18 @@ export class DonatePage extends Component {
<Grid className='donate-page-wrapper'> <Grid className='donate-page-wrapper'>
<Spacer /> <Spacer />
<Row> <Row>
<Col sm={10} smOffset={1} xs={12}> <Fragment>
<h1 className='text-center'> <Col lg={6} lgOffset={0} md={8} mdOffset={2} sm={10} smOffset={1}>
{isDonating <Row className='donate-text'>
? 'Thank You for Your Support' <Col className={'text-center'} xs={12}>
: 'Become a Supporter'} {isDonating ? (
</h1> <h2>Thank you for your support</h2>
) : (
<h2>Help us do more</h2>
)}
<Spacer /> <Spacer />
</Col> </Col>
</Row> </Row>
<Row>
<Fragment>
<Col md={6}>
<Row>
<Col sm={10} smOffset={1} xs={12}>
{isDonating ? ( {isDonating ? (
<Alert> <Alert>
<p> <p>
@ -101,20 +101,21 @@ export class DonatePage extends Component {
</p> </p>
<br /> <br />
<p> <p>
If you would like to make additional donations, those If you would like to make additional donations, those will
will help our nonprofit and our mission, too. help our nonprofit and our mission, too.
</p> </p>
</Alert> </Alert>
) : null} ) : null}
</Col> <DonateText isDonating={isDonating} />
</Row> <Spacer />
<DonateForm <DonateForm
enableDonationSettingsPage={this.enableDonationSettingsPage} enableDonationSettingsPage={this.enableDonationSettingsPage}
handleProcessing={this.handleProcessing} handleProcessing={this.handleProcessing}
/> />
<DonateSupportText />
</Col> </Col>
<Col md={6}> <Col lg={6}>
<DonateText /> <CampersImage page='donate' />
</Col> </Col>
</Fragment> </Fragment>
</Row> </Row>

View File

@ -1,18 +1,15 @@
// Configuration for client side // Configuration for client side
const durationsConfig = { const durationsConfig = {
year: 'yearly',
month: 'monthly', month: 'monthly',
onetime: 'one-time' onetime: 'one-time'
}; };
const amountsConfig = { const amountsConfig = {
year: [100000, 25000, 6000], month: [1000, 2000, 3000, 4000, 5000],
month: [25000, 3500, 500], onetime: [2500, 5000, 7500, 10000, 15000]
onetime: [100000, 25000, 6000]
}; };
const defaultAmount = { const defaultAmount = {
year: 25000, month: 1000,
month: 500, onetime: 2500
onetime: 25000
}; };
const defaultDonation = { const defaultDonation = {
donationAmount: defaultAmount['month'], donationAmount: defaultAmount['month'],
@ -25,79 +22,54 @@ const modalDefaultDonation = {
const onetimeSKUConfig = { const onetimeSKUConfig = {
live: [ live: [
{ amount: '100000', id: 'sku_GwHogRRJrCYGms' }, { amount: '15000', id: 'sku_IElisJHup0nojP' },
{ amount: '25000', id: 'sku_GwHnCde23uDH5R' }, { amount: '10000', id: 'sku_IEliodY88lglPk' },
{ amount: '6000', id: 'sku_H5mjFgpayAzJzT' } { amount: '7500', id: 'sku_IEli9AXW8DwNtT' },
{ amount: '5000', id: 'sku_IElhJxkNh9UgDp' },
{ amount: '2500', id: 'sku_IElhQtqLgKZC8y' }
], ],
staging: [ staging: [
{ amount: '100000', id: 'sku_GvAeUdWLsmGO9O' }, { amount: '15000', id: 'sku_IEPNpHACYJmUwz' },
{ amount: '25000', id: 'sku_GvAdXbsotjFi7G' }, { amount: '10000', id: 'sku_IEPMY1OXxnY4WU' },
{ amount: '6000', id: 'sku_GvAeJDgwjnGAdy' } { amount: '7500', id: 'sku_IEPLOotEqlMOWC' },
{ amount: '5000', id: 'sku_IEPKAxxAxfMnUI' },
{ amount: '2500', id: 'sku_IEPIgLRzViwq5z' }
] ]
}; };
// Configuration for server side // Configuration for server side
const durationKeysConfig = ['year', 'month', 'onetime']; const durationKeysConfig = ['month', 'onetime'];
const donationOneTimeConfig = [100000, 25000, 6000]; const donationOneTimeConfig = [100000, 25000, 6000];
const donationSubscriptionConfig = { const donationSubscriptionConfig = {
duration: { duration: {
year: 'Yearly',
month: 'Monthly' month: 'Monthly'
}, },
plans: { plans: {
year: [100000, 25000, 6000],
month: [25000, 3500, 500] month: [25000, 3500, 500]
} }
}; };
// Shared paypal configuration // Shared paypal configuration
// keep the 5 dollars for the modal
const paypalConfigTypes = { const paypalConfigTypes = {
live: { live: {
month: { month: {
'500': { '500': { planId: 'P-1L11422374370240ULZKX3PA' },
planId: 'P-1L11422374370240ULZKX3PA' '1000': { planId: 'P-61K21421WY874920PL6E36YI' },
}, '2000': { planId: 'P-31999436LF709112VL6E374A' },
'3500': { '3000': { planId: 'P-1KY930839N8045117L6E4BKY' },
planId: 'P-81U00703FF076883HLZ2PWMI' '4000': { planId: 'P-0JW4843250567551AL6E4CAI' },
}, '5000': { planId: 'P-0WR49877YD949401BL6E4CTA' }
'25000': {
planId: 'P-7M045671FN915794KLZ2PW6I'
}
},
year: {
'6000': {
planId: 'P-9Y661558DW462253NLZZ2IMQ'
},
'25000': {
planId: 'P-3NN39392MK1889318LZZ2KQY'
},
'100000': {
planId: 'P-7YN43286C4599382LLZZ2JUI'
}
} }
}, },
staging: { staging: {
month: { month: {
'500': { '500': { planId: 'P-37N14480BW163382FLZYPVMA' },
planId: 'P-37N14480BW163382FLZYPVMA' '1000': { planId: 'P-28B62039J8092810UL6E3FXA' },
}, '2000': { planId: 'P-7HR706961M9170433L6HI5VI' },
'3500': { '3000': { planId: 'P-35V33574BU596924JL6HI6XY' },
planId: 'P-3E678937P5715503NLZZTRVY' '4000': { planId: 'P-45M45060289267734L6HJSXA' },
}, '5000': { planId: 'P-0MD70861FY4172444L6HJTUQ' }
'25000': {
planId: 'P-97K80194AU368022JLZ2Q27Y'
}
},
year: {
'6000': {
planId: 'P-0UY77185EM3077131LZYP6VY'
},
'25000': {
planId: 'P-7K1585908S634694XLZZTHUQ'
},
'100000': {
planId: 'P-0J5231134H608574XLZZTDLQ'
}
} }
} }
}; };