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 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5818f43
Change to dev.v2.js for local development
caleballdrin Nov 20, 2024
874f3fd
Add use-v3 config variable
caleballdrin Nov 20, 2024
ac2d5d9
EP-2519 - Add styles for reordering gift selection on useV3. (#1118)
wjames111 Nov 27, 2024
41840af
EP-2525 - Hide unnecessary fields (#1120)
wjames111 Dec 16, 2024
4ec9576
Reorders gift selection to frequency/dates/amount. (#1127)
wjames111 Dec 17, 2024
1522b6a
Add submitOrder() service
caleballdrin Dec 19, 2024
70f2559
Add submitOrderInternal and helper functions to branded checkout step 1
caleballdrin Dec 19, 2024
315bd48
Remove submitOrderInternal() from step 3 and use orderService.submitO…
caleballdrin Dec 19, 2024
e3818f6
Add recaptcha button and loading if using V3
caleballdrin Dec 19, 2024
4408c96
Remove componentInstance from Recaptcha and use apply() to make Angul…
caleballdrin Dec 19, 2024
545412e
Move variables to cartEvents.js to avoid circular dependency
caleballdrin Dec 19, 2024
1b24d26
Move checkout error messages to a new component
caleballdrin Dec 19, 2024
346ee7b
Merge remote-tracking branch 'origin/EP-2517-branded-checkout-improve…
caleballdrin Dec 19, 2024
405bd6e
Merge pull request #1119 from CruGlobal/EP-2518-remove-confirmation-step
caleballdrin Dec 20, 2024
d964703
EP-2560 (#1129)
wjames111 Jan 7, 2025
806d875
EP-2560-QA-1 (#1131)
wjames111 Jan 9, 2025
1852e9e
Suggested amount button design for branded checkout v3
rguinee Jan 10, 2025
270183d
Styles for suggested amount buttons for branded checkout v3
rguinee Jan 10, 2025
122207f
Fix codestyle in emailField.tpl.html.
wjames111 Jan 13, 2025
67586aa
fix styling for first and last name.
wjames111 Jan 13, 2025
015c7fa
account for no phone number on the form when adding custom validators…
wjames111 Jan 13, 2025
53cf9d1
Merge pull request #1133 from CruGlobal/EP-2560-QA-2
wjames111 Jan 14, 2025
72d00e3
Merge pull request #1116 from CruGlobal/EP-2521-Radio-Button-redesign-1
rguinee Jan 15, 2025
9896968
Merge branch 'master' into EP-2517-branded-checkout-improvements (pri…
wrandall22 Jan 16, 2025
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"
>
wrandall22 marked this conversation as resolved.
Show resolved Hide resolved
</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