From 1cead2f0863c14071b22108f284dce44f96d56f1 Mon Sep 17 00:00:00 2001 From: Ahmad Abdolsaheb Date: Tue, 17 Dec 2019 18:56:30 +0300 Subject: [PATCH] feat(donate): add year end gift page --- .../YearEndGift/YearEndDonationForm.js | 347 ++++++++++++++++++ .../components/YearEndGift/YearEndGift.css | 18 + client/src/components/YearEndGift/index.js | 24 ++ client/src/components/layouts/global.css | 2 + client/src/pages/year-end-gift.js | 16 + 5 files changed, 407 insertions(+) create mode 100644 client/src/components/YearEndGift/YearEndDonationForm.js create mode 100644 client/src/components/YearEndGift/YearEndGift.css create mode 100644 client/src/components/YearEndGift/index.js create mode 100644 client/src/pages/year-end-gift.js diff --git a/client/src/components/YearEndGift/YearEndDonationForm.js b/client/src/components/YearEndGift/YearEndDonationForm.js new file mode 100644 index 0000000000..d1ab5f6bd8 --- /dev/null +++ b/client/src/components/YearEndGift/YearEndDonationForm.js @@ -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 ( +
+ {paymentType === 'Card' ? ( + + + + + + ) : ( +

+ PayPal is currently unavailable. Please use a Credit/Debit card + instead. +

+ )} +
+ ); + } + + 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 ( +
+ + +
+ ); + } + + renderCustomAmountInput() { + return ( +
+ + Or give a custom amount: + + + +
+ ); + } + + renderPayPalDonations() { + return ( +
+ + + +
+ ); + } + + renderForm(item) { + return ( +
+ {' '} + {' '} + +
+ ); + } + + handleClick() { + this.setState({ showOtherAmounts: true, donationAmount: 25000 }); + } + + renderOtherPaymentButton() { + return ( + <> + + + + ); + } + + render() { + const { isDonating } = this.props; + if (isDonating) { + return ( + + + + + + ); + } + + console.log(this.state.donationAmount); + + return ( + + + + Thank you again for supporting freeCodeCamp.org with a one-time year + end gift. Please enter your credit card information below. + + + + + {this.renderAmountRadio()} + + + {this.state.showOtherAmounts + ? this.renderCustomAmountInput() + : this.renderOtherPaymentButton()} + + + + {this.renderDonationOptions()} + + + + Or give using PayPal: + + + {this.renderPayPalDonations()} + + + + + + If you need a receipt from your taxes, reply to Quincy's email he + sent you. + + + + ); + } +} + +YearEndDonationForm.displayName = 'YearEndDonationForm'; +YearEndDonationForm.propTypes = propTypes; + +export default connect( + mapStateToProps, + null +)(YearEndDonationForm); diff --git a/client/src/components/YearEndGift/YearEndGift.css b/client/src/components/YearEndGift/YearEndGift.css new file mode 100644 index 0000000000..4f6ef4b025 --- /dev/null +++ b/client/src/components/YearEndGift/YearEndGift.css @@ -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; +} diff --git a/client/src/components/YearEndGift/index.js b/client/src/components/YearEndGift/index.js new file mode 100644 index 0000000000..2b1ac011c6 --- /dev/null +++ b/client/src/components/YearEndGift/index.js @@ -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 ( + +
+ + +

Become a Supporter

+ +
+ + +
+
+ ); +}; + +DonateText.displayName = ''; + +export default DonateText; diff --git a/client/src/components/layouts/global.css b/client/src/components/layouts/global.css index a87f7a9f42..eed50c0bf5 100644 --- a/client/src/components/layouts/global.css +++ b/client/src/components/layouts/global.css @@ -162,6 +162,8 @@ input[type='submit'], button:hover, input[type='submit']:hover, .btn:hover, +.btn-default.active:hover, +.btn-default.active, .btn-primary:hover { border-color: var(--secondary-color); background-color: var(--secondary-color); diff --git a/client/src/pages/year-end-gift.js b/client/src/pages/year-end-gift.js new file mode 100644 index 0000000000..36bb584cd9 --- /dev/null +++ b/client/src/pages/year-end-gift.js @@ -0,0 +1,16 @@ +import React from 'react'; +import Helmet from 'react-helmet'; +import YearEndGift from '../components/YearEndGift'; + +function YearEndGiftPage() { + return ( + <> + + + + ); +} + +YearEndGiftPage.displayName = 'YearEndGiftPage'; + +export default YearEndGiftPage;