From 3b07c7445d90557038e695b1749164b5cf2812fe Mon Sep 17 00:00:00 2001 From: daniel joo Date: Wed, 30 Jun 2021 15:07:27 -0700 Subject: [PATCH 1/8] added the braintree web drop for react into json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 032b333..e627369 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@types/react": "^16.9.35", "@types/react-router-dom": "^5.1.5", "bootstrap": "^4.5.0", + "braintree-web-drop-in-react": "^1.2.1", "file-loader": "5.0.2", "jquery": "^3.5.1", "jwt-decode": "^2.2.0", From 806e36932fe27e258b76714d22c818e6ca804d40 Mon Sep 17 00:00:00 2001 From: daniel joo Date: Wed, 30 Jun 2021 15:24:35 -0700 Subject: [PATCH 2/8] made empty donation page and added to the header --- src/App.tsx | 2 ++ src/components/Donate.tsx | 22 ++++++++++++++++++++++ src/components/Header.tsx | 6 +++--- 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 src/components/Donate.tsx diff --git a/src/App.tsx b/src/App.tsx index 237404f..9f1239d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,7 @@ import Home from './components/Home'; import AboutUs from './components/AboutUs'; import ContactUs from './components/ContactUs'; import Issue from './components/Issue'; +import Donate from './components/Donate'; window.onload = () => { ReactGA.initialize('G-H1CSQJ6BGQ'); @@ -44,6 +45,7 @@ class App extends React.Component<{}, State, {}> { } /> } /> } /> + } /> } diff --git a/src/components/Donate.tsx b/src/components/Donate.tsx new file mode 100644 index 0000000..5b8280b --- /dev/null +++ b/src/components/Donate.tsx @@ -0,0 +1,22 @@ +import React, { Component } from 'react'; +import { Helmet } from 'react-helmet'; +import { withAlert } from 'react-alert'; + +// image imports + +class Donate extends Component<{}, {}> { + render() { + return ( +
+ + Welcome + + + Hello! + +
+ ); + } +} + +export default withAlert()(Donate); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index d4b29b7..2ec349a 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -86,12 +86,12 @@ class Header extends Component<{}, {}, {}> { Contact Us -
  • - +
  • + - +
  • From 497317cfb3138636be911349f068ec29bba76876 Mon Sep 17 00:00:00 2001 From: daniel joo Date: Wed, 30 Jun 2021 20:47:09 -0700 Subject: [PATCH 3/8] implemented UI skeleton --- src/components/Donate.tsx | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/components/Donate.tsx b/src/components/Donate.tsx index 5b8280b..b2adae8 100644 --- a/src/components/Donate.tsx +++ b/src/components/Donate.tsx @@ -4,7 +4,12 @@ import { withAlert } from 'react-alert'; // image imports -class Donate extends Component<{}, {}> { +class Donate extends Component<{}, {amount: number}> { + constructor(props) { + super(props); + this.state = { amount: 0 }; + } + render() { return (
    @@ -12,8 +17,17 @@ class Donate extends Component<{}, {}> { Welcome - Hello! - +
    Hello! Click a button or put in your own amount
    +
    + + + + +
    + + +
    +
    ); } From ea85586f216da86e445e639b25bf04ebd44e9b23 Mon Sep 17 00:00:00 2001 From: daniel joo Date: Wed, 30 Jun 2021 23:47:15 -0700 Subject: [PATCH 4/8] implented store --- .eslintrc.json | 3 +- src/App.tsx | 2 ++ src/components/Header.tsx | 4 +-- src/components/Store.tsx | 61 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 src/components/Store.tsx diff --git a/.eslintrc.json b/.eslintrc.json index d50577a..02011d8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -40,6 +40,7 @@ "jsx-a11y/anchor-is-valid": 1, "react/no-did-update-set-state": "off", "max-classes-per-file": 1, - "no-unused-vars": "off" + "no-unused-vars": "off" , + "no-return-assign": "off" } } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 9f1239d..39996db 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,6 +16,7 @@ import AboutUs from './components/AboutUs'; import ContactUs from './components/ContactUs'; import Issue from './components/Issue'; import Donate from './components/Donate'; +import Store from './components/Store'; window.onload = () => { ReactGA.initialize('G-H1CSQJ6BGQ'); @@ -46,6 +47,7 @@ class App extends React.Component<{}, State, {}> { } /> } /> } /> + } /> } diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 2ec349a..2ae6fc5 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -87,9 +87,9 @@ class Header extends Component<{}, {}, {}> {
  • - +
  • diff --git a/src/components/Store.tsx b/src/components/Store.tsx new file mode 100644 index 0000000..528c741 --- /dev/null +++ b/src/components/Store.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import DropIn from 'braintree-web-drop-in-react'; +import getServerURL from '../serverOverride'; + +class Store extends React.Component<{}, {clientToken: string, amount: string}> { + instance; + + constructor(props) { + super(props); + this.state = { clientToken: '', amount: '5' }; + } + + componentDidMount() { + // Get a client token for authorization from your server + fetch(`${getServerURL()}/generate-client-token`, { method: 'GET' }) + .then((response) => response.text()).then((responseJSON) => { + console.log(responseJSON); + this.setState({ clientToken: responseJSON }); + }); + } + + async buy() { + // Send the nonce to your server + const { nonce } = await this.instance.requestPaymentMethod(); + fetch(`${getServerURL()}/checkout-donation`, { + method: 'POST', + credentials: 'include', + body: JSON.stringify({ + payment_method_nonce: nonce, + amount: this.state.amount, + }), + }) + .then((response) => response.json()) + .then((responseJSON) => { + if (responseJSON.status === 'SUCCESS') { + console.log('yay'); + } + }); + } + + render() { + if (!this.state.clientToken) { + return ( +
    +

    Loading...

    +
    + ); + } + return ( +
    + (this.instance = instance)} + /> + +
    + ); + } +} + +export default Store; From 81491716ccfb19f7964651150c16d53b56907e7a Mon Sep 17 00:00:00 2001 From: daniel joo Date: Mon, 5 Jul 2021 17:10:42 -0700 Subject: [PATCH 5/8] finished ugly skeleton with some errors --- src/App.tsx | 2 - src/components/Donate.tsx | 118 ++++++++++++++++++++++++++++---------- src/components/Header.tsx | 4 +- src/components/Store.tsx | 61 -------------------- 4 files changed, 89 insertions(+), 96 deletions(-) delete mode 100644 src/components/Store.tsx diff --git a/src/App.tsx b/src/App.tsx index 39996db..9f1239d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,7 +16,6 @@ import AboutUs from './components/AboutUs'; import ContactUs from './components/ContactUs'; import Issue from './components/Issue'; import Donate from './components/Donate'; -import Store from './components/Store'; window.onload = () => { ReactGA.initialize('G-H1CSQJ6BGQ'); @@ -47,7 +46,6 @@ class App extends React.Component<{}, State, {}> { } /> } /> } /> - } /> } diff --git a/src/components/Donate.tsx b/src/components/Donate.tsx index b2adae8..c19fcd4 100644 --- a/src/components/Donate.tsx +++ b/src/components/Donate.tsx @@ -1,36 +1,92 @@ -import React, { Component } from 'react'; +import React from 'react'; +import DropIn from 'braintree-web-drop-in-react'; import { Helmet } from 'react-helmet'; -import { withAlert } from 'react-alert'; - -// image imports - -class Donate extends Component<{}, {amount: number}> { - constructor(props) { - super(props); - this.state = { amount: 0 }; - } - - render() { - return ( -
    - - Welcome - - -
    Hello! Click a button or put in your own amount
    -
    - - - - -
    - - +import getServerURL from '../serverOverride'; + +class Donate extends React.Component<{}, {clientToken: string, amount: string, showDropin: boolean}> { + instance; + + constructor(props) { + super(props); + this.state = { clientToken: '', amount: '5', showDropin: false }; + } + + componentDidMount() { + // Get a client token for authorization from your server + fetch(`${getServerURL()}/generate-client-token`, { method: 'GET' }) + .then((response) => response.text()).then((responseJSON) => { + console.log(responseJSON); + this.setState({ clientToken: responseJSON }); + }); + } + + async buy() { + // Send the nonce to your server + const { nonce } = await this.instance.requestPaymentMethod(); + fetch(`${getServerURL()}/checkout-donation`, { + method: 'POST', + credentials: 'include', + body: JSON.stringify({ + payment_method_nonce: nonce, + amount: this.state.amount, + }), + }) + .then((response) => response.json()) + .then((responseJSON) => { + if (responseJSON.status === 'SUCCESS') { + console.log('yay'); + } + }); + } + + render() { + let dropInComponent; + if (!this.state.showDropin) { + dropInComponent =
    ; + } else if (!this.state.clientToken) { + dropInComponent = ( +
    +

    Loading...

    + ); + } else { + dropInComponent = ( +
    + (this.instance = instance)} + /> + +
    + ); + console.log(this.state.showDropin); + } + + return ( +
    +
    + + Welcome + + +
    Hello! Click a button or put in your own amount
    +
    + + + + +
    + + +
    +
    +
    + {dropInComponent}
    -
    - ); - } + ); + } } -export default withAlert()(Donate); +// card: 4111 1111 1111 1111, year: 01/24 + +export default Donate; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 2ae6fc5..c06d8f6 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -87,9 +87,9 @@ class Header extends Component<{}, {}, {}> {
  • - +
  • diff --git a/src/components/Store.tsx b/src/components/Store.tsx deleted file mode 100644 index 528c741..0000000 --- a/src/components/Store.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import DropIn from 'braintree-web-drop-in-react'; -import getServerURL from '../serverOverride'; - -class Store extends React.Component<{}, {clientToken: string, amount: string}> { - instance; - - constructor(props) { - super(props); - this.state = { clientToken: '', amount: '5' }; - } - - componentDidMount() { - // Get a client token for authorization from your server - fetch(`${getServerURL()}/generate-client-token`, { method: 'GET' }) - .then((response) => response.text()).then((responseJSON) => { - console.log(responseJSON); - this.setState({ clientToken: responseJSON }); - }); - } - - async buy() { - // Send the nonce to your server - const { nonce } = await this.instance.requestPaymentMethod(); - fetch(`${getServerURL()}/checkout-donation`, { - method: 'POST', - credentials: 'include', - body: JSON.stringify({ - payment_method_nonce: nonce, - amount: this.state.amount, - }), - }) - .then((response) => response.json()) - .then((responseJSON) => { - if (responseJSON.status === 'SUCCESS') { - console.log('yay'); - } - }); - } - - render() { - if (!this.state.clientToken) { - return ( -
    -

    Loading...

    -
    - ); - } - return ( -
    - (this.instance = instance)} - /> - -
    - ); - } -} - -export default Store; From 201a1df8d021ae5aac29f76227b24d234b59fb95 Mon Sep 17 00:00:00 2001 From: daniel joo Date: Mon, 5 Jul 2021 17:38:32 -0700 Subject: [PATCH 6/8] minor ui changes --- src/components/Donate.tsx | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/components/Donate.tsx b/src/components/Donate.tsx index c19fcd4..ae43f86 100644 --- a/src/components/Donate.tsx +++ b/src/components/Donate.tsx @@ -56,28 +56,30 @@ class Donate extends React.Component<{}, {clientToken: string, amount: string, s options={{ authorization: this.state.clientToken }} onInstance={(instance) => (this.instance = instance)} /> - +
    ); console.log(this.state.showDropin); } return ( -
    +
    Welcome -
    Hello! Click a button or put in your own amount
    -
    - - - - -
    - - +
    +
    Hello! Click a button or put in your own amount
    +
    + + + + +
    + + +
    @@ -89,4 +91,4 @@ class Donate extends React.Component<{}, {clientToken: string, amount: string, s // card: 4111 1111 1111 1111, year: 01/24 -export default Donate; +export default (Donate); From 0efe7a185b22ff5469ad7bc085e211acbe9ec757 Mon Sep 17 00:00:00 2001 From: daniel joo Date: Mon, 26 Jul 2021 16:00:27 -0700 Subject: [PATCH 7/8] updated the process to 4 steps --- src/components/Donate.tsx | 131 ++++++++++++++++++++++++++++++-------- 1 file changed, 103 insertions(+), 28 deletions(-) diff --git a/src/components/Donate.tsx b/src/components/Donate.tsx index ae43f86..a6894fe 100644 --- a/src/components/Donate.tsx +++ b/src/components/Donate.tsx @@ -1,27 +1,34 @@ import React from 'react'; import DropIn from 'braintree-web-drop-in-react'; import { Helmet } from 'react-helmet'; +// import Braintree from 'braintree-web'; import getServerURL from '../serverOverride'; -class Donate extends React.Component<{}, {clientToken: string, amount: string, showDropin: boolean}> { +import RectangleSVG from '../static/images/story2.png'; +import handsSVG from '../static/images/valuesHero.png'; + +class Donate extends React.Component<{}, {clientToken: string, amount: string, processing: number, pageNum:number}> { instance; constructor(props) { super(props); - this.state = { clientToken: '', amount: '5', showDropin: false }; + this.state = { + clientToken: '', amount: '5', processing: 0, pageNum: 1, + }; } componentDidMount() { // Get a client token for authorization from your server fetch(`${getServerURL()}/generate-client-token`, { method: 'GET' }) .then((response) => response.text()).then((responseJSON) => { - console.log(responseJSON); + // console.log(responseJSON); this.setState({ clientToken: responseJSON }); }); } async buy() { // Send the nonce to your server + this.setState({ processing: 1 }); const { nonce } = await this.instance.requestPaymentMethod(); fetch(`${getServerURL()}/checkout-donation`, { method: 'POST', @@ -34,16 +41,30 @@ class Donate extends React.Component<{}, {clientToken: string, amount: string, s .then((response) => response.json()) .then((responseJSON) => { if (responseJSON.status === 'SUCCESS') { - console.log('yay'); + console.log('yay'); // this doesn't happen } }); + this.setState({ pageNum: 4 }); } render() { let dropInComponent; - if (!this.state.showDropin) { - dropInComponent =
    ; - } else if (!this.state.clientToken) { + let buyButton; + let hostedFieldsPage; + let output; + + if (this.state.processing === 0) { + buyButton = ; + } else if (this.state.processing === 1) { + buyButton = ( + + ); + } + + if (!this.state.clientToken) { dropInComponent = (

    Loading...

    @@ -51,39 +72,93 @@ class Donate extends React.Component<{}, {clientToken: string, amount: string, s ); } else { dropInComponent = ( -
    +
    (this.instance = instance)} /> - + {buyButton}
    ); - console.log(this.state.showDropin); } - return ( -
    -
    - - Welcome - - -
    -
    Hello! Click a button or put in your own amount
    -
    - - - - -
    - - + const welcomePage = ( +
    +
    +
    Your donation makes a difference
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    1. Select Amount
    +
    + + + + + +
    + +
    +
    +
    - {dropInComponent} +
    + ); + + const thankYouPage = ( +
    +
    Thank you for your donation
    + Extra text about where the donation goes +
    + +
    +
    + ); + + if (this.state.pageNum === 1) { + output = welcomePage; + } else if (this.state.pageNum === 2) { + output = hostedFieldsPage; + } else if (this.state.pageNum === 3) { + output = ( +
    +
    +
    + (this.instance = instance)} + /> +
    +
    +
    {buyButton}
    +
    + ); + // doing {dropInComponent} doesn't work? + } else if (this.state.pageNum === 4) { + output = thankYouPage; + } + + return ( +
    + + Welcome + + + {output}
    ); } From a90715447c6b26ab996f386553e630930e886421 Mon Sep 17 00:00:00 2001 From: daniel joo Date: Wed, 4 Aug 2021 19:33:13 -0700 Subject: [PATCH 8/8] started trying out new braintree for hosted fields --- package.json | 1 + src/App.tsx | 4 +- src/components/Donate.tsx | 2 +- src/components/Donate2.tsx | 214 +++++++++++++++++++++++++++++++++++++ src/components/Header.tsx | 2 +- 5 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 src/components/Donate2.tsx diff --git a/package.json b/package.json index e627369..829de45 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "react-alert-template-basic": "^1.0.0", "react-anchor-link-smooth-scroll": "^1.0.12", "react-bootstrap": "^1.3.0", + "react-braintree-fields": "^1.6.0", "react-dom": "^16.13.1", "react-ga": "^3.3.0", "react-google-recaptcha": "^2.1.0", diff --git a/src/App.tsx b/src/App.tsx index 9f1239d..30295c0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,7 +15,7 @@ import Home from './components/Home'; import AboutUs from './components/AboutUs'; import ContactUs from './components/ContactUs'; import Issue from './components/Issue'; -import Donate from './components/Donate'; +import Donate2 from './components/Donate2'; window.onload = () => { ReactGA.initialize('G-H1CSQJ6BGQ'); @@ -45,7 +45,7 @@ class App extends React.Component<{}, State, {}> { } /> } /> } /> - } /> + } /> } diff --git a/src/components/Donate.tsx b/src/components/Donate.tsx index a6894fe..51de799 100644 --- a/src/components/Donate.tsx +++ b/src/components/Donate.tsx @@ -1,7 +1,7 @@ import React from 'react'; import DropIn from 'braintree-web-drop-in-react'; import { Helmet } from 'react-helmet'; -// import Braintree from 'braintree-web'; +import { Braintree, HostedField } from 'react-braintree-fields'; import getServerURL from '../serverOverride'; import RectangleSVG from '../static/images/story2.png'; diff --git a/src/components/Donate2.tsx b/src/components/Donate2.tsx new file mode 100644 index 0000000..ca3fc64 --- /dev/null +++ b/src/components/Donate2.tsx @@ -0,0 +1,214 @@ +// import { any } from 'prop-types'; +// import React from 'react'; +// import { Braintree, HostedField } from 'react-braintree-fields'; + +// class Donate2 extends React.PureComponent<{},{token:any, amount:string}>{ +// instance; +// braintree: any; +// numberField: any; + +// constructor(props) { +// super(props); +// this.braintree = React.createRef(); +// this.numberField = React.createRef(); +// this.state = { +// token: null, +// amount: '5', +// } +// } + +// //cannot use this since the braintree uses the token not as a string +// // componentDidMount() { +// // // Get a client token for authorization from your server +// // console.log('got here'); +// // fetch(`${getServerURL()}/generate-client-token`, { method: 'GET' }) +// // .then((response) => response.text()).then((responseJSON) => { +// // // console.log(responseJSON); +// // this.setState({ clientToken: responseJSON }); +// // }); +// // console.log(this.state.clientToken); +// // } + +// //this compiles, sets the token as an object instead of a string + +// // componentDidMount(){ +// // fetch('${getServerURL()}/generate-client-token', {method:'GET'}) +// // .then((response) => { +// // this.setState({token:response}); +// // }); +// // console.log('got a token'); +// // } + +// async buy() { +// // Send the nonce to your server +// const { nonce } = await this.instance.requestPaymentMethod(); +// fetch(`${getServerURL()}/checkout-donation`, { +// method: 'POST', +// credentials: 'include', +// body: JSON.stringify({ +// payment_method_nonce: nonce, +// amount: this.state.amount, +// }), +// }) +// .then((response) => response.json()) +// .then((responseJSON) => { +// if (responseJSON.status === 'SUCCESS') { +// console.log('yay'); // this doesn't happen +// } +// }); +// } + +// render() { +// return ( +//
    +//

    Braintree Hosted Fields Demo

    + +// this.state.token} +// authorization = 'sandbox_gpr679yy_tj4vmtfr3qqmc9yb' +// styles={{ +// input: { +// 'font-size': '14px', +// 'font-family': 'helvetica, tahoma, calibri, sans-serif', +// color: '#7d6b6b', +// }, +// ':focus': { +// color: 'black', +// }, +// }} +// > +//
    +// (this.ccNum = ccNum)} /> +// +// +//
    +//
    +//
    +// +//
    +//
    +// ); +// } + +// } + +// export default (Donate2); + +import React from 'react'; +import { Braintree, HostedField } from 'react-braintree-fields'; +import getServerURL from '../serverOverride'; + +const Donate2 = () => { + const [tokenize, setTokenizeFunc] = React.useState(); + const [cardType, setCardType] = React.useState(''); + const [error, setError] = React.useState(null); + const [token, setToken] = React.useState(null); + const [focusedFieldName, setFocusedField] = React.useState(''); + const numberField = React.useRef(); + const cvvField = React.useRef(); + const cardholderNameField = React.useRef(); + + const handleError = (newError) => { + setError(newError.message || String(newError)); + }; + + const onFieldBlur = (field, event) => setFocusedField(''); + const onFieldFocus = (field, event) => setFocusedField(event.emittedBy); + + const getAuthorization = () => { + fetch(`${getServerURL()}/generate-client-token`, { method: 'GET' }) + .then((response) => response.text()).then((responseJSON) => { + console.log(responseJSON); + return responseJSON; + }); + }; + + const getToken = () => { + // @ts-ignore: Object is possibly 'null'. + tokenize() + .then(setToken) + .catch(handleError); + }; + + const renderResult = (title, obj) => { + if (!obj) { return null; } + return ( +
    + + {title} + : + +
    {JSON.stringify(obj, null, 4)}
    +
    + ); + }; + + return ( +
    + setTokenizeFunc(() => ref)} + styles={{ + input: { + 'font-size': 'inherit', + }, + ':focus': { + color: 'blue', + }, + }} + > + {renderResult('Error', error)} + {renderResult('Token', token)} + +
    + Number: + +

    + Card type: + {cardType} +

    + Name: + + Date: + + CVV: + + Zip: + +
    + +
    +
    + +
    +
    + ); +}; + +export default Donate2; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index c06d8f6..fb8227b 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -87,7 +87,7 @@ class Header extends Component<{}, {}, {}> {
  • - +