- freeCodeCamp is completely free. But it costs our nonprofit a lot of
- money to run it. Help us pay for servers. Set up a tax-deductible
- monthly donation you can afford.
-
-
-
{this.renderAmountButtons()}
+
+
+
+ freeCodeCamp.org is a tiny nonprofit that's helping millions of
+ people learn to code for free.
+
+
+ Join 4,180 supporters.
+
+
+ Your $5 / month donation will help keep tech education free and
+ open.
+
+
{this.renderEmailInput()}
-
- {this.props.maybeButton()}
-
+
+
);
}
renderEmailInput() {
- const { email } = this.state;
return (
- Email where we should send your donation tax receipt:
+ Your Email (we'll send you a tax-deductible donation receipt):
diff --git a/client/src/components/Donation/components/StripeCardForm.js b/client/src/components/Donation/components/StripeCardForm.js
index 70ca22b56a..f538a84019 100644
--- a/client/src/components/Donation/components/StripeCardForm.js
+++ b/client/src/components/Donation/components/StripeCardForm.js
@@ -1,4 +1,4 @@
-import React, { Component } from 'react';
+import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import {
CardNumberElement,
@@ -17,7 +17,7 @@ const style = {
}
};
-class StripCardForm extends Component {
+class StripCardForm extends PureComponent {
constructor(...props) {
super(...props);
@@ -74,15 +74,15 @@ class StripCardForm extends Component {
return (
- Card Number:
+ Your Card Number:
- Card Expiry:
+ Your Card Expiration Month:
- Card CVC (3-digit security number):
+ Your Card CVC (3-digit security number):
diff --git a/client/src/components/icons/poweredByStripe.js b/client/src/components/icons/poweredByStripe.js
new file mode 100644
index 0000000000..3084e6e18d
--- /dev/null
+++ b/client/src/components/icons/poweredByStripe.js
@@ -0,0 +1,31 @@
+/* eslint-disable max-len */
+import React from 'react';
+
+function PoweredByStripe() {
+ return (
+
+
+
+
+
+ );
+}
+
+PoweredByStripe.displayName = 'PoweredByStripe';
+
+export default PoweredByStripe;
diff --git a/client/src/pages/donate.js b/client/src/pages/donate.js
new file mode 100644
index 0000000000..b79961aa66
--- /dev/null
+++ b/client/src/pages/donate.js
@@ -0,0 +1,108 @@
+/* eslint-disable max-len */
+import React, { Component, Fragment } from 'react';
+import PropTypes from 'prop-types';
+import Helmet from 'react-helmet';
+import { connect } from 'react-redux';
+import { StripeProvider, Elements } from 'react-stripe-elements';
+import { createSelector } from 'reselect';
+
+import { userSelector } from '../redux';
+
+import Spacer from '../components/helpers/Spacer';
+import DonateForm from '../components/Donation/components/DonateForm';
+import DonateCompletion from '../components/Donation/components/DonateCompletion';
+import PoweredByStripe from '../components/icons/poweredByStripe';
+
+import './index.css';
+
+const propTypes = {
+ email: PropTypes.string,
+ show: PropTypes.bool
+};
+
+const mapStateToProps = createSelector(userSelector, ({ email = '' }) => ({
+ email
+}));
+
+// Stripe public key
+const stripeKey = 'pk_live_E6Z6xPM8pEsJziHW905zpAvF';
+
+class IndexPage extends Component {
+ constructor(...props) {
+ super(...props);
+ this.state = {
+ stripe: null
+ };
+
+ this.handleStripeLoad = this.handleStripeLoad.bind(this);
+ }
+ componentDidMount() {
+ if (window.Stripe) {
+ /* eslint-disable react/no-did-mount-set-state */
+ this.setState(state => ({
+ ...state,
+ stripe: window.Stripe(stripeKey)
+ }));
+ } else {
+ document
+ .querySelector('#stripe-js')
+ .addEventListener('load', this.handleStripeLoad);
+ }
+ }
+
+ componentWillUnmount() {
+ const stripeMountPoint = document.querySelector('#stripe-js');
+
+ if (stripeMountPoint) {
+ stripeMountPoint.removeEventListener('load', this.handleStripeLoad);
+ }
+ }
+
+ handleStripeLoad() {
+ // Create Stripe instance once Stripe.js loads
+ console.info('stripe has loaded');
+ this.setState(state => ({
+ ...state,
+ stripe: window.Stripe(stripeKey)
+ }));
+ }
+
+ renderCompletion(props) {
+ return
{}} {...props} />;
+ }
+ render() {
+ const { email = '' } = this.props;
+ return (
+
+ );
+ }
+}
+
+IndexPage.displayName = 'IndexPage';
+IndexPage.propTypes = propTypes;
+
+export default connect(mapStateToProps)(IndexPage);