feat(donate): add year end gift page
This commit is contained in:
committed by
mrugesh
parent
8cca639450
commit
1cead2f086
347
client/src/components/YearEndGift/YearEndDonationForm.js
Normal file
347
client/src/components/YearEndGift/YearEndDonationForm.js
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
/* eslint-disable react/sort-prop-types */
|
||||||
|
/* eslint-disable react/jsx-sort-props */
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import {
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
ControlLabel,
|
||||||
|
FormControl,
|
||||||
|
FormGroup,
|
||||||
|
Button
|
||||||
|
} from '@freecodecamp/react-bootstrap';
|
||||||
|
import { StripeProvider, Elements } from 'react-stripe-elements';
|
||||||
|
import { Spacer } from '../helpers';
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
import DonateFormChildViewForHOC from '../Donation/components/DonateFormChildViewForHOC';
|
||||||
|
import { userSelector } from '../../redux';
|
||||||
|
|
||||||
|
import './YearEndGift.css';
|
||||||
|
import '../Donation/Donation.css';
|
||||||
|
import DonateCompletion from '../Donation/components/DonateCompletion.js';
|
||||||
|
import { stripePublicKey } from '../../../../config/env.json';
|
||||||
|
import { stripeScriptLoader } from '../../utils/scriptLoaders';
|
||||||
|
|
||||||
|
const defaultYearEndStateConfig = {
|
||||||
|
donationAmount: 25000,
|
||||||
|
donationDuration: 'onetime',
|
||||||
|
paymentType: 'Card'
|
||||||
|
};
|
||||||
|
|
||||||
|
const numToCommas = num =>
|
||||||
|
num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
showCloseBtn: PropTypes.func,
|
||||||
|
defaultTheme: PropTypes.string,
|
||||||
|
isDonating: PropTypes.bool,
|
||||||
|
stripe: PropTypes.shape({
|
||||||
|
createToken: PropTypes.func.isRequired
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = createSelector(
|
||||||
|
userSelector,
|
||||||
|
({ isDonating }) => ({
|
||||||
|
isDonating
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
class YearEndDonationForm extends Component {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
this.state = {
|
||||||
|
...defaultYearEndStateConfig,
|
||||||
|
showOtherAmounts: false,
|
||||||
|
isDonating: this.props.isDonating,
|
||||||
|
stripe: null
|
||||||
|
};
|
||||||
|
this.handleSelectPaymentType = this.handleSelectPaymentType.bind(this);
|
||||||
|
this.handleStripeLoad = this.handleStripeLoad.bind(this);
|
||||||
|
this.getDonationButtonLabel = this.getDonationButtonLabel.bind(this);
|
||||||
|
this.handleSelectAmount = this.handleSelectAmount.bind(this);
|
||||||
|
this.handleChange = this.handleChange.bind(this);
|
||||||
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (window.Stripe) {
|
||||||
|
this.handleStripeLoad();
|
||||||
|
} else if (document.querySelector('#stripe-js')) {
|
||||||
|
document
|
||||||
|
.querySelector('#stripe-js')
|
||||||
|
.addEventListener('load', this.handleStripeLoad);
|
||||||
|
} else {
|
||||||
|
stripeScriptLoader(this.handleStripeLoad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const stripeMountPoint = document.querySelector('#stripe-js');
|
||||||
|
if (stripeMountPoint) {
|
||||||
|
stripeMountPoint.removeEventListener('load', this.handleStripeLoad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleStripeLoad() {
|
||||||
|
// Create Stripe instance once Stripe.js loads
|
||||||
|
if (stripePublicKey) {
|
||||||
|
this.setState(state => ({
|
||||||
|
...state,
|
||||||
|
stripe: window.Stripe(stripePublicKey)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSelectPaymentType(e) {
|
||||||
|
this.setState({
|
||||||
|
paymentType: e.currentTarget.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormatedAmountLabel(amount) {
|
||||||
|
return `$${numToCommas(amount / 100)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDonationButtonLabel() {
|
||||||
|
const { donationAmount, donationDuration } = this.state;
|
||||||
|
let donationBtnLabel = `Confirm your donation`;
|
||||||
|
if (donationDuration === 'onetime') {
|
||||||
|
donationBtnLabel = `Confirm your one-time donation of ${this.getFormatedAmountLabel(
|
||||||
|
donationAmount
|
||||||
|
)}`;
|
||||||
|
} else {
|
||||||
|
donationBtnLabel = `Confirm your donation of ${this.getFormatedAmountLabel(
|
||||||
|
donationAmount
|
||||||
|
)} ${donationDuration === 'month' ? 'per month' : 'per year'}`;
|
||||||
|
}
|
||||||
|
return donationBtnLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDonationOptions() {
|
||||||
|
const {
|
||||||
|
donationAmount,
|
||||||
|
donationDuration,
|
||||||
|
paymentType,
|
||||||
|
stripe
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const { showCloseBtn, defaultTheme } = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{paymentType === 'Card' ? (
|
||||||
|
<StripeProvider stripe={stripe}>
|
||||||
|
<Elements>
|
||||||
|
<DonateFormChildViewForHOC
|
||||||
|
showCloseBtn={showCloseBtn}
|
||||||
|
defaultTheme={defaultTheme}
|
||||||
|
donationAmount={donationAmount}
|
||||||
|
donationDuration={donationDuration}
|
||||||
|
getDonationButtonLabel={this.getDonationButtonLabel}
|
||||||
|
/>
|
||||||
|
</Elements>
|
||||||
|
</StripeProvider>
|
||||||
|
) : (
|
||||||
|
<p>
|
||||||
|
PayPal is currently unavailable. Please use a Credit/Debit card
|
||||||
|
instead.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSelectAmount(e) {
|
||||||
|
this.setState({ donationAmount: Number(e.target.value) });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange(e) {
|
||||||
|
if (isNaN(e.target.value)) return;
|
||||||
|
const amount = Math.floor(e.target.value) * 100;
|
||||||
|
this.setState({ donationAmount: amount });
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAmountRadio() {
|
||||||
|
return (
|
||||||
|
<form className='radio-container'>
|
||||||
|
<label>Suggested gift amounts:</label>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
value={100000}
|
||||||
|
checked={this.state.donationAmount === 100000}
|
||||||
|
onChange={this.handleSelectAmount}
|
||||||
|
/>
|
||||||
|
$1,000
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
value={25000}
|
||||||
|
checked={this.state.donationAmount === 25000}
|
||||||
|
onChange={this.handleSelectAmount}
|
||||||
|
/>
|
||||||
|
$250
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
value={10000}
|
||||||
|
checked={this.state.donationAmount === 10000}
|
||||||
|
onChange={this.handleSelectAmount}
|
||||||
|
/>
|
||||||
|
$100
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCustomAmountInput() {
|
||||||
|
return (
|
||||||
|
<form>
|
||||||
|
<FormGroup controlId='formBasicText'>
|
||||||
|
<ControlLabel>Or give a custom amount:</ControlLabel>
|
||||||
|
<FormControl
|
||||||
|
type='text'
|
||||||
|
value={this.state.donationAmount / 100}
|
||||||
|
placeholder='Enter Amount'
|
||||||
|
onChange={this.handleChange}
|
||||||
|
/>
|
||||||
|
<FormControl.Feedback />
|
||||||
|
</FormGroup>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPayPalDonations() {
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
action='https://www.paypal.com/cgi-bin/webscr'
|
||||||
|
method='post'
|
||||||
|
target='_top'
|
||||||
|
>
|
||||||
|
<input type='hidden' name='cmd' value='_s-xclick' />
|
||||||
|
<input type='hidden' name='hosted_button_id' value='9C73W6CWSLNPW' />
|
||||||
|
<input
|
||||||
|
type='submit'
|
||||||
|
name='submit'
|
||||||
|
className='btn btn-block'
|
||||||
|
title='PayPal - The safer, easier way to pay online!'
|
||||||
|
alt='Donate with PayPal button'
|
||||||
|
value='Make a PayPal donation'
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderForm(item) {
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
action='https://www.paypal.com/cgi-bin/webscr'
|
||||||
|
method='post'
|
||||||
|
target='_blank'
|
||||||
|
>
|
||||||
|
<input defaultValue='_s-xclick' name='cmd' type='hidden' />{' '}
|
||||||
|
<input
|
||||||
|
defaultValue={item.defaultValueHash}
|
||||||
|
name='hosted_button_id'
|
||||||
|
type='hidden'
|
||||||
|
/>{' '}
|
||||||
|
<input
|
||||||
|
className='btn btn-block'
|
||||||
|
value={item.defaultValue}
|
||||||
|
name='submit'
|
||||||
|
type='submit'
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick() {
|
||||||
|
this.setState({ showOtherAmounts: true, donationAmount: 25000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOtherPaymentButton() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button className='btn-link' onClick={this.handleClick}>
|
||||||
|
<b>Or give a custom amount</b>
|
||||||
|
</Button>
|
||||||
|
<Spacer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isDonating } = this.props;
|
||||||
|
if (isDonating) {
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<DonateCompletion success={true} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(this.state.donationAmount);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<b>
|
||||||
|
Thank you again for supporting freeCodeCamp.org with a one-time year
|
||||||
|
end gift. Please enter your credit card information below.
|
||||||
|
</b>
|
||||||
|
<Spacer />
|
||||||
|
</Col>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
{this.renderAmountRadio()}
|
||||||
|
</Col>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
{this.state.showOtherAmounts
|
||||||
|
? this.renderCustomAmountInput()
|
||||||
|
: this.renderOtherPaymentButton()}
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
{this.renderDonationOptions()}
|
||||||
|
<Spacer />
|
||||||
|
</Col>
|
||||||
|
<Col sm={10} smOffset={1} xs={12} style={{ marginBottom: '5px' }}>
|
||||||
|
<b>Or give using PayPal:</b>
|
||||||
|
</Col>
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
{this.renderPayPalDonations()}
|
||||||
|
<Spacer />
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col sm={10} smOffset={1} xs={12}>
|
||||||
|
<b>
|
||||||
|
If you need a receipt from your taxes, reply to Quincy's email he
|
||||||
|
sent you.
|
||||||
|
</b>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
YearEndDonationForm.displayName = 'YearEndDonationForm';
|
||||||
|
YearEndDonationForm.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null
|
||||||
|
)(YearEndDonationForm);
|
18
client/src/components/YearEndGift/YearEndGift.css
Normal file
18
client/src/components/YearEndGift/YearEndGift.css
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
.radio-container ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-container li label {
|
||||||
|
padding: 7px 20px 6px 0px;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-container li label input {
|
||||||
|
height: 17px;
|
||||||
|
width: 17px;
|
||||||
|
margin: auto 5px auto 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
24
client/src/components/YearEndGift/index.js
Normal file
24
client/src/components/YearEndGift/index.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Grid } from '@freecodecamp/react-bootstrap';
|
||||||
|
import { Spacer, FullWidthRow } from '../../components/helpers';
|
||||||
|
import YearEndDonationForm from './YearEndDonationForm';
|
||||||
|
|
||||||
|
const DonateText = () => {
|
||||||
|
return (
|
||||||
|
<Grid>
|
||||||
|
<main>
|
||||||
|
<Spacer />
|
||||||
|
<FullWidthRow>
|
||||||
|
<h1 className='text-center'>Become a Supporter</h1>
|
||||||
|
<YearEndDonationForm />
|
||||||
|
</FullWidthRow>
|
||||||
|
<Spacer />
|
||||||
|
<Spacer />
|
||||||
|
</main>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
DonateText.displayName = '';
|
||||||
|
|
||||||
|
export default DonateText;
|
@ -162,6 +162,8 @@ input[type='submit'],
|
|||||||
button:hover,
|
button:hover,
|
||||||
input[type='submit']:hover,
|
input[type='submit']:hover,
|
||||||
.btn:hover,
|
.btn:hover,
|
||||||
|
.btn-default.active:hover,
|
||||||
|
.btn-default.active,
|
||||||
.btn-primary:hover {
|
.btn-primary:hover {
|
||||||
border-color: var(--secondary-color);
|
border-color: var(--secondary-color);
|
||||||
background-color: var(--secondary-color);
|
background-color: var(--secondary-color);
|
||||||
|
16
client/src/pages/year-end-gift.js
Normal file
16
client/src/pages/year-end-gift.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Helmet from 'react-helmet';
|
||||||
|
import YearEndGift from '../components/YearEndGift';
|
||||||
|
|
||||||
|
function YearEndGiftPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Helmet title='Support our nonprofit | freeCodeCamp.org' />
|
||||||
|
<YearEndGift />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
YearEndGiftPage.displayName = 'YearEndGiftPage';
|
||||||
|
|
||||||
|
export default YearEndGiftPage;
|
Reference in New Issue
Block a user