Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EP-2517 Branded Checkout Improvements #1117

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions branded-checkout.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
</head>
<body>

<branded-checkout designation-number="2294554" show-cover-fees="true" ng-cloak></branded-checkout>
<script src="branded-checkout.v2.js"></script>
<branded-checkout designation-number="2294554" show-cover-fees="true" use-v3="true" ng-cloak></branded-checkout>
<script src="dev.v2.js"></script>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to change this back before we merge.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there mention of JSON config file for these options or did I make that up? Are we just using the use-v3 flag for everything?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no config file, we use this flag for everything.


</body>
</html>
12 changes: 9 additions & 3 deletions src/app/branded/branded-checkout.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,13 @@ class BrandedCheckoutController {
next () {
switch (this.checkoutStep) {
case 'giftContactPayment':
this.checkoutStep = 'review'
this.fireAnalyticsEvents('review')
// If it is a single step form, the next step should be 'thankYou'
if (this.useV3 === 'true') {
this.checkoutStep = 'thankYou'
} else {
this.checkoutStep = 'review'
this.fireAnalyticsEvents('review')
}
break
case 'review':
this.checkoutStep = 'thankYou'
Expand Down Expand Up @@ -181,6 +186,7 @@ export default angular
onOrderCompleted: '&',
onOrderFailed: '&',
language: '@',
showCoverFees: '@'
showCoverFees: '@',
useV3: '@'
}
})
3 changes: 2 additions & 1 deletion src/app/branded/branded-checkout.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
next="$ctrl.next()"
on-payment-failed="$ctrl.onPaymentFailed($event.donorDetails)"
radio-station-api-url="$ctrl.radioStationApiUrl"
radio-station-radius="$ctrl.radioStationRadius">
radio-station-radius="$ctrl.radioStationRadius"
use-v3="$ctrl.useV3">
</branded-checkout-step-1>
<branded-checkout-step-2
ng-if="$ctrl.checkoutStep === 'review'"
Expand Down
121 changes: 117 additions & 4 deletions src/app/branded/step-1/branded-checkout-step-1.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,29 @@ import every from 'lodash/every'
import productConfigForm from 'app/productConfig/productConfigForm/productConfigForm.component'
import contactInfo from 'common/components/contactInfo/contactInfo.component'
import checkoutStep2 from 'app/checkout/step-2/step-2.component'
import checkoutErrorMessages from 'app/checkout/checkout-error-messages/checkout-error-messages.component'

import cartService from 'common/services/api/cart.service'
import orderService from 'common/services/api/order.service'
import analyticsFactory from '../../analytics/analytics.factory'
import brandedAnalyticsFactory from '../../branded/analytics/branded-analytics.factory'

import { FEE_DERIVATIVE } from 'common/components/paymentMethods/coverFees/coverFees.component'

import template from './branded-checkout-step-1.tpl.html'
import 'rxjs/add/operator/catch'
import 'rxjs/add/operator/do'
import 'rxjs/add/operator/finally'

const componentName = 'brandedCheckoutStep1'

class BrandedCheckoutStep1Controller {
/* @ngInject */
constructor ($log, $filter, brandedAnalyticsFactory, cartService, orderService) {
constructor ($scope, $log, $filter, $window, analyticsFactory, brandedAnalyticsFactory, cartService, orderService) {
this.$scope = $scope
this.$log = $log
this.$filter = $filter
this.$window = $window
this.analyticsFactory = analyticsFactory
this.brandedAnalyticsFactory = brandedAnalyticsFactory
this.cartService = cartService
this.orderService = orderService
Expand Down Expand Up @@ -154,21 +161,123 @@ class BrandedCheckoutStep1Controller {
checkSuccessfulSubmission () {
if (every(this.submission, 'completed')) {
if (every(this.submission, { error: false })) {
this.next()
if (this.useV3 === 'true') {
this.submitOrderInternal()
} else {
this.next()
}
} else {
this.submitted = false
}
}
}

loadCart () {
this.errorLoadingCart = false

const cart = this.cartService.get()
cart.subscribe(
data => {
// Setting cart data and analytics
this.cartData = data
this.brandedAnalyticsFactory.saveCoverFees(this.orderService.retrieveCoverFeeDecision())
if (this.cartData && this.cartData.items) {
this.brandedAnalyticsFactory.saveItem(this.cartData.items[0])
}
this.brandedAnalyticsFactory.addPaymentInfo()
},
error => {
// Handle errors by setting flag and logging the error
this.errorLoadingCart = true
this.$log.error('Error loading cart data for branded checkout (single step)', error)
}
)
return cart
}

loadCurrentPayment () {
this.loadingCurrentPayment = true

const getCurrentPayment = this.orderService.getCurrentPayment()
getCurrentPayment.finally(() => {
this.loadingCurrentPayment = false
}).subscribe(
data => {
if (!data) {
this.$log.error('Error loading current payment info: current payment doesn\'t seem to exist')
} else if (data['account-type']) {
this.bankAccountPaymentDetails = data
} else if (data['card-type']) {
this.creditCardPaymentDetails = data
} else {
this.$log.error('Error loading current payment info: current payment type is unknown')
}
},
error => {
this.$log.error('Error loading current payment info', error)
}
)
return getCurrentPayment
}

checkErrors () {
// Then check for errors on the API
return this.orderService.checkErrors().do(
(data) => {
this.needinfoErrors = data
})
.catch(error => {
this.$log.error('Error loading checkErrors', error)
})
}

submitOrderInternal () {
this.loadingAndSubmitting = true
this.loadCart()
.mergeMap(() => {
return this.loadCurrentPayment()
})
.mergeMap(() => {
return this.checkErrors()
})
.mergeMap(() => {
return this.orderService.submitOrder(this)
})
.finally(() => {
this.loadingAndSubmitting = false
})
.subscribe(() => {
this.next()
})
}

handleRecaptchaFailure () {
this.analyticsFactory.checkoutFieldError('submitOrder', 'failed')
this.submittingOrder = false
this.loadingAndSubmitting = false
this.onSubmittingOrder({ value: false })

this.loadCart()

this.onSubmitted()
this.submissionError = 'generic error'
this.$window.scrollTo(0, 0)
}

canSubmitOrder () {
return !this.submittingOrder
}
}

export default angular
.module(componentName, [
productConfigForm.name,
contactInfo.name,
checkoutStep2.name,
checkoutErrorMessages.name,
cartService.name,
orderService.name,
analyticsFactory.name,
brandedAnalyticsFactory.name
])
.component(componentName, {
Expand All @@ -188,6 +297,10 @@ export default angular
next: '&',
onPaymentFailed: '&',
radioStationApiUrl: '<',
radioStationRadius: '<'
radioStationRadius: '<',
onSubmittingOrder: '&',
onSubmitted: '&',
useV3: '<',
caleballdrin marked this conversation as resolved.
Show resolved Hide resolved
loadingAndSubmitting: '<'
}
})
34 changes: 29 additions & 5 deletions src/app/branded/step-1/branded-checkout-step-1.tpl.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<checkout-error-messages
needinfo-errors="$ctrl.needinfoErrors"
submission-error="$ctrl.submissionError"
submission-error-status="$ctrl.submissionErrorStatus">
</checkout-error-messages>
<div class="panel">
<div class="panel-body loading-overlay-parent">
<loading ng-if="$ctrl.loadingProductConfig">
Expand All @@ -11,7 +16,9 @@
submitted="$ctrl.submitted"
on-state-change="$ctrl.onGiftConfigStateChange(state)"
disable-session-restart="true"
ng-if="!$ctrl.loadingProductConfig && !$ctrl.errorLoadingProductConfig">
ng-if="!$ctrl.loadingProductConfig && !$ctrl.errorLoadingProductConfig"
use-v3="$ctrl.useV3"
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's put this > up on the line above.

</product-config-form>
<div ng-if="$ctrl.errorLoadingProductConfig" class="alert alert-danger" role="alert">
<p translate='LOADING_ERROR' translate-values="{loadData: '$ctrl.loadData()'}" translate-compile></p>
Expand All @@ -21,13 +28,14 @@
<div ng-if="!$ctrl.loadingProductConfig && !$ctrl.errorLoadingProductConfig">
<div class="panel">
<div class="panel-body">
<h3 class="panel-name" translate>{{'YOUR_INFORMATION'}}</h3>
<h3 ng-if="$ctrl.useV3 !== 'true'" class="panel-name" translate>{{'YOUR_INFORMATION'}}</h3>
<contact-info
submitted="$ctrl.submitted"
on-submit="$ctrl.onContactInfoSubmit(success)"
donor-details="$ctrl.donorDetails"
radio-station-api-url="$ctrl.radioStationApiUrl"
radio-station-radius="$ctrl.radioStationRadius">
radio-station-radius="$ctrl.radioStationRadius"
use-v3="$ctrl.useV3">
</contact-info>
</div>
</div>
Expand All @@ -46,10 +54,26 @@ <h3 class="panel-name" translate>{{'PAYMENT'}}</h3>
</div>
<div class="panel">
<div class="panel-body text-right">
<button class="btn btn-primary"
<div class="checkout-cta pull-right" ng-if="$ctrl.useV3 === 'true'">
<recaptcha-wrapper
action="'branded_submit'"
on-success="$ctrl.submit"
on-failure="$ctrl.handleRecaptchaFailure"
component-instance="$ctrl"
button-id="'submitOrderButton'"
button-type="'submit'"
button-classes="'btn btn-primary btn-lg btn-block'"
button-disabled="$ctrl.errorLoadingProductConfig || !$ctrl.canSubmitOrder()"
button-label="'SUBMIT_GIFT'"></recaptcha-wrapper>
</div>

<button ng-if="$ctrl.useV3 !== 'true'" class="btn btn-primary"
ng-click="$ctrl.submit()"
ng-disabled="$ctrl.errorLoadingProductConfig"
translate>{{'CONTINUE'}}</button>
</div>
</div>
</div>
</div>
<loading type="fixed" ng-if="$ctrl.loadingAndSubmitting">
<translate>{{'SUBMITTING_GIFT'}}</translate>
</loading>
2 changes: 1 addition & 1 deletion src/app/cart/cart.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import productModalService from 'common/services/productModal.service'
import desigSrcDirective from 'common/directives/desigSrc.directive'

import displayRateTotals from 'common/components/displayRateTotals/displayRateTotals.component'
import { cartUpdatedEvent } from 'common/components/nav/navCart/navCart.component'
import { cartUpdatedEvent } from 'common/lib/cartEvents'

import analyticsFactory from 'app/analytics/analytics.factory'

Expand Down
2 changes: 1 addition & 1 deletion src/app/cart/cart.component.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Observable } from 'rxjs/Observable'
import 'rxjs/add/observable/of'
import 'rxjs/add/observable/throw'

import { cartUpdatedEvent } from 'common/components/nav/navCart/navCart.component'
import { cartUpdatedEvent } from 'common/lib/cartEvents'

describe('cart', () => {
beforeEach(angular.mock.module(module.name))
Expand Down
11 changes: 6 additions & 5 deletions src/app/checkout/cart-summary/cart-summary.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,22 @@ export const submitOrderEvent = 'submitOrderEvent'

class CartSummaryController {
/* @ngInject */
constructor (cartService, $scope) {
constructor (cartService, $scope, $rootScope) {
this.$scope = $scope
this.$rootScope = $rootScope
this.cartService = cartService
}

buildCartUrl () {
return this.cartService.buildCartUrl()
}

handleRecaptchaFailure (componentInstance) {
componentInstance.$rootScope.$emit(recaptchaFailedEvent)
handleRecaptchaFailure () {
this.$rootScope.$emit(recaptchaFailedEvent)
}

onSubmit (componentInstance) {
componentInstance.$rootScope.$emit(submitOrderEvent)
onSubmit () {
this.$rootScope.$emit(submitOrderEvent)
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/app/checkout/cart-summary/cart-summary.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ describe('checkout', function () {

describe('onSubmit', () => {
it('should emit an event', () => {
jest.spyOn(componentInstance.$rootScope, '$emit').mockImplementation(() => {})
self.controller.onSubmit(componentInstance)
expect(componentInstance.$rootScope.$emit).toHaveBeenCalledWith(submitOrderEvent)
jest.spyOn(self.controller.$rootScope, '$emit').mockImplementation(() => {})
self.controller.onSubmit()
expect(self.controller.$rootScope.$emit).toHaveBeenCalledWith(submitOrderEvent)
})
})

describe('handleRecaptchaFailure', () => {
it('should emit an event', () => {
jest.spyOn(componentInstance.$rootScope, '$emit').mockImplementation(() => {})
self.controller.handleRecaptchaFailure(componentInstance)
expect(componentInstance.$rootScope.$emit).toHaveBeenCalledWith(recaptchaFailedEvent)
jest.spyOn(self.controller.$rootScope, '$emit').mockImplementation(() => {})
self.controller.handleRecaptchaFailure()
expect(self.controller.$rootScope.$emit).toHaveBeenCalledWith(recaptchaFailedEvent)
})
})
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import angular from 'angular'

import template from './checkout-error-messages.tpl.html'

const componentName = 'checkoutErrorMessages'

class CheckoutErrorMessagesController {}

export default angular
.module(componentName, [])
.component(componentName, {
controller: CheckoutErrorMessagesController,
templateUrl: template,
bindings: {
needinfoErrors: '<',
submissionError: '<',
submissionErrorStatus: '<'
}
})
Loading