Skip to content

Commit

Permalink
74 add spinner to create billing agreement (#86)
Browse files Browse the repository at this point in the history
* Add spinner to Subscriptions component
* Handle errors in create/execute billing agreement
* Extract types, get tests passing

- create billing agreement failure
- execute billing agreement failure
  • Loading branch information
mattwr18 authored and bryszard committed Apr 13, 2019
1 parent 09142c0 commit a3637b6
Show file tree
Hide file tree
Showing 20 changed files with 451 additions and 180 deletions.
3 changes: 1 addition & 2 deletions README.MD
Expand Up @@ -27,6 +27,7 @@ Run tests
```
$ yarn test
```

# Rebuild the semantic UI asserts
## This command needs to be run with npm - yarn does not support interactive prompt installs
`# npm install --save-dev semantic-ui`
Expand All @@ -45,8 +46,6 @@ and copy the folder to our src folder

`cp -R dist/* ../src/assets/`

----

### [Code of Conduct](./CODE_OF_CONDUCT).
### [Contribution Guide](./CONTRIBUTION_GUIDE).

Expand Down
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -28,18 +28,19 @@
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"global": "^4.3.2",
"izitoast": "^1.4.0",
"paypal-checkout": "^4.0.254",
"query-string": "^6.2.0",
"react": "16.8.0",
"react-cookie": "^3.0.8",
"react-dom": "16.8.0",
"react-loading-overlay": "^1.0.1",
"react-redux": "^6.0.0",
"react-router-dom": "^4.3.1",
"react-select": "^2.3.0",
"react-spinners": "^0.5.1",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"semantic-ui-react": "^0.84.0"
"semantic-ui-react": "^0.84.0",
"url": "^0.11.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
Expand Down
24 changes: 24 additions & 0 deletions src/actions/createBillingAgreement.js
@@ -0,0 +1,24 @@
import axios from 'axios'
import { CREATE_BILLING_AGREEMENT_FAILURE } from '../types'

export default (cookies, id, dispatch) => event => {
event.preventDefault()
return axios({
method: 'POST',
timeout: 30000,
url: '/paypal/new.json',
data: {
plan: id
},
headers: {
Authorization: cookies.get('_WebsiteOne_session')
}
})
.then(response => window.location.assign(response.data.redirect_url))
.catch(error => {
dispatch({
type: CREATE_BILLING_AGREEMENT_FAILURE,
message: error.message
})
})
}
23 changes: 23 additions & 0 deletions src/actions/executeBillingAgreement.js
@@ -0,0 +1,23 @@
import axios from 'axios'
import { EXECUTE_BILLING_AGREEMENT_FAILURE } from '../types'

export default (cookies, params, dispatch) => {
return axios({
method: 'GET',
timeout: 20000,
url: '/paypal/create',
params: {
plan: params.plan,
token: params.token
},
headers: {
Authorization: cookies.get('_WebsiteOne_session')
}
})
.catch(error => {
dispatch({
type: EXECUTE_BILLING_AGREEMENT_FAILURE,
message: error.message
})
})
}
4 changes: 2 additions & 2 deletions src/components/PayPalAgreementNew.js
@@ -1,10 +1,10 @@
import React from 'react'
import { Segment, Header } from 'semantic-ui-react'

export default ({ cookies, createBillingAgreement, plan }) => (
export default ({ cookies, createBillingAgreement, setLoading, plan, dispatch }) => (
<Segment padded='very' className='paypal-section' raised>
<Header as='h5'>Get {plan.name} via Paypal:</Header>
<form onSubmit={createBillingAgreement(cookies, plan.id)}>
<form onClick={setLoading} onSubmit={createBillingAgreement(cookies, plan.id, dispatch)}>
<input
type='image'
name='submit'
Expand Down
48 changes: 30 additions & 18 deletions src/components/PayPalSuccess.js
@@ -1,38 +1,50 @@
import React, { useEffect, Fragment } from 'react'
import React, { useEffect, Fragment, useState } from 'react'
import { connect } from 'react-redux'
import executeBillingAgreement from '../helpers/executeBillingAgreement'
import executeBillingAgreement from '../actions/executeBillingAgreement'
import { Header, Container, Segment } from 'semantic-ui-react'
import queryString from 'query-string'
import membership from '../helpers/membershipInfo'
import ErrorBoundary from './ErrorBoundary'
import '../assets/PayPalSuccess.css'

export const PayPalSuccess = props => {
const params = queryString.parse(props.location.search)
const [error, setError] = useState(false)
let name = membership(props, queryString).name
useEffect(() => {
executeBillingAgreement(props, params)
if (props.error.length) {
setError(true)
} else {
executeBillingAgreement(props.cookies, params, props.dispatch)
}
})
return (
<Fragment>
<Container>
<Segment padded='very' className='payment-complete' raised>
<Header as='h2' textAlign='center'>
Thanks, you're now an AgileVentures {}
{params.plan.charAt(0).toUpperCase() + params.plan.slice(1)} Member!
</Header>
<Header as='h4' textAlign='center'>
Your 7 day free trial has now started. Your card will not be charged
until 7 days have passed.
</Header>
<Header as='h5' textAlign='center'>
An AgileVentures mentor will be in touch shortly to help you receive
all of your membership benefits.
</Header>
</Segment>
{!error
? <Segment padded='very' className='payment-complete' raised>
<Header as='h2' textAlign='center'>
Thanks, you're now an AgileVentures {}
{name}{' '}
Member!
</Header>
<Header as='h4' textAlign='center'>
{name === 'Premium' ? 'Your 7 day free trial has now started. Your card will not be charged until 7 days have passed.' : null}
</Header>
<Header as='h5' textAlign='center'>
An AgileVentures mentor will be in touch shortly to help you
receive all of your membership benefits.
</Header>
</Segment> : <ErrorBoundary error />}
</Container>
</Fragment>
)
}

const mapStateToProps = (_, ownProps) => ({ cookies: ownProps.cookies })
const mapStateToProps = (store, ownProps) => ({
cookies: ownProps.cookies,
error: store.error
})
export default connect(
mapStateToProps,
null
Expand Down
92 changes: 51 additions & 41 deletions src/components/Subscriptions.js
@@ -1,69 +1,79 @@
import React, { Fragment, useEffect } from 'react'
import React, { Fragment, useEffect, useState } from 'react'
import { connect } from 'react-redux'
import { setLastLocation } from '../actions/setLastLocationAction'
import { Header, Container, Segment, Grid } from 'semantic-ui-react'
import PayPalAgreementNew from './PayPalAgreementNew'
import createBillingAgreement from '../helpers/createBillingAgreement'
import createBillingAgreement from '../actions/createBillingAgreement'
import queryString from 'query-string'
import membership from '../helpers/membershipInfo'
import LoadingOverlay from 'react-loading-overlay'
import { RingLoader } from 'react-spinners'
import ErrorBoundary from './ErrorBoundary'
import '../assets/Subscriptions.css'

let membership = props => {
let info
let plan = queryString.parse(props.location.search).plan
if (plan === 'premiummob') {
info = { id: 2, name: 'Premium Mob', price: '£25.00' }
} else if (plan === 'premiumf2f') {
info = { id: 3, name: 'Premium F2F', price: '£50.00' }
} else {
info = { id: 1, name: 'Premium', price: '£10.00' }
}
return info
}
export const Subscriptions = props => {
let name = membership(props).name

let name = membership(props, queryString).name
const [loading, setLoading] = useState(false)
const [error, setError] = useState(false)
useEffect(() => {
const path = props.location.pathname
const search = props.location.search
props.setLastLocation(path, search)
if (!props.loggedInUser || !props.cookies.get('_WebsiteOne_session')) {
if (!props.cookies.get('_WebsiteOne_session') && !props.loggedInUser.data) {
props.history.push({ pathname: '/login' })
}
if (props.error.length) {
setError(true)
}
})

return (
<Fragment>
<Container>
<Header as='h1'>AgileVentures {name} Membership</Header>
<Header as='h5'>
The price for {name} Membership is {membership(props).price}/Month
</Header>
<Header as='h5'>
{name === 'Premium' ? '7 day free trial! No charge for 7 days' : null}
</Header>
<Grid columns={2} divided className='payment-section'>
<Grid.Row>
<Grid.Column>
<PayPalAgreementNew
cookies={props.cookies}
createBillingAgreement={createBillingAgreement}
plan={membership(props)}
/>
</Grid.Column>
<Grid.Column>
<Segment>
<Header as='h5'>Get {name} via Credit/Debit Card:</Header>
</Segment>
</Grid.Column>
</Grid.Row>
</Grid>
{!error ? <LoadingOverlay
active={loading}
styles={{
overlay: (base) => ({
...base,
background: 'rbg(255, 255, 255, 0.3)'
})
}}
text={<RingLoader sizeUnit={'px'} size={200} color={'#ee7335'} />}
>
<Header as='h1'>AgileVentures {name} Membership</Header>
<Header as='h5'>
The price for {name} Membership is {membership(props, queryString).price}/Month
</Header>
<Header as='h5'>
{name === 'Premium' ? '7 day free trial! No charge for 7 days' : null}
</Header>
<Grid columns={2} divided className='payment-section'>
<Grid.Row>
<Grid.Column>
<PayPalAgreementNew
cookies={props.cookies}
createBillingAgreement={createBillingAgreement}
plan={membership(props, queryString)}
setLoading={setLoading}
dispatch={props.dispatch}
/>
</Grid.Column>
<Grid.Column>
<Segment>
<Header as='h5'>Get {name} via Credit/Debit Card:</Header>
</Segment>
</Grid.Column>
</Grid.Row>
</Grid>
</LoadingOverlay> : <ErrorBoundary error />}
</Container>
</Fragment>
)
}
const mapStateToProps = (store, ownProps) => ({
loggedInUser: store.loggedInUser,
cookies: ownProps.cookies
cookies: ownProps.cookies,
error: store.error
})
export default connect(
mapStateToProps,
Expand Down
4 changes: 1 addition & 3 deletions src/components/User.js
Expand Up @@ -25,9 +25,7 @@ const User = ({ item: user }) => {
</big>
</Link>
<Card.Description>
{user.title_list.length
? user.title_list.map(title => title + ' ')
: null}
{user.title_list.map(title => title + ' ')}
</Card.Description>
<p className='user-card-footer'>
<Icon name='fire' /> {}
Expand Down
12 changes: 12 additions & 0 deletions src/helpers/membershipInfo.js
@@ -0,0 +1,12 @@
export default (props, queryString) => {
let info
let plan = queryString.parse(props.location.search).plan
if (plan === 'premiummob') {
info = { id: 2, name: 'Premium Mob', price: '£25.00' }
} else if (plan === 'premiumf2f') {
info = { id: 3, name: 'Premium F2F', price: '£50.00' }
} else {
info = { id: 1, name: 'Premium', price: '£10.00' }
}
return info
}
2 changes: 1 addition & 1 deletion src/index.js
Expand Up @@ -4,8 +4,8 @@ import { BrowserRouter } from 'react-router-dom'
import { CookiesProvider } from 'react-cookie'
import { Provider } from 'react-redux'
import store from './store'
import './assets/semantic.css'
import App from './components/App'
import './assets/semantic.css'

render(
<CookiesProvider>
Expand Down
12 changes: 10 additions & 2 deletions src/reducers/errorReducer.js
@@ -1,10 +1,18 @@
import { FETCH_PROJECTS_FAILURE } from '../types'
import {
FETCH_PROJECTS_FAILURE,
CREATE_BILLING_AGREEMENT_FAILURE,
EXECUTE_BILLING_AGREEMENT_FAILURE
} from '../types'
import initialState from './initialState'

const errorReducer = (state = initialState.error, action) => {
switch (action.type) {
case FETCH_PROJECTS_FAILURE:
return [ action.message ]
return [action.message]
case CREATE_BILLING_AGREEMENT_FAILURE:
return [action.message]
case EXECUTE_BILLING_AGREEMENT_FAILURE:
return [action.message]
default:
return state
}
Expand Down

0 comments on commit a3637b6

Please sign in to comment.