diff --git a/package.json b/package.json index e7329d9cf1..d867113bd0 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "postcss-loader": "^2.0.6", "prettier-eslint-cli": "^4.3.2", "prop-types": "^15.5.10", - "query-string": "^6.4.0", + "query-string": "^6.5.0", "raf": "^3.4.1", "ramda": "^0.26.1", "raven-js": "^3.26.4", diff --git a/static/js/containers/pages/CheckoutPage.js b/static/js/containers/pages/CheckoutPage.js index cbd3aceaf2..dc274876c9 100644 --- a/static/js/containers/pages/CheckoutPage.js +++ b/static/js/containers/pages/CheckoutPage.js @@ -2,15 +2,17 @@ import React from "react" import * as R from "ramda" import { connect } from "react-redux" -import { connectRequest, mutateAsync } from "redux-query" +import { mutateAsync, requestAsync } from "redux-query" import { compose } from "redux" +import queryString from "query-string" import queries from "../../lib/queries" import { calculateDiscount, calculatePrice, formatPrice, - formatRunTitle + formatRunTitle, + formatErrors } from "../../lib/ecommerce" import { createCyberSourceForm } from "../../lib/form" @@ -64,6 +66,26 @@ export class CheckoutPage extends React.Component { errors: null } + componentDidMount = async () => { + const { + fetchBasket, + location: { search } + } = this.props + const params = queryString.parse(search) + const productId = parseInt(params.product) + if (!productId) { + await fetchBasket() + return + } + + await this.updateBasket({ items: [{ id: productId }] }) + + const couponCode = params.code + if (couponCode) { + await this.updateBasket({ coupons: [{ code: couponCode }] }) + } + } + handleErrors = async (responsePromise: Promise<*>) => { const response = await responsePromise if (response.body.errors) { @@ -202,13 +224,14 @@ export class CheckoutPage extends React.Component { const { basket } = this.props const { couponCode, errors } = this.state - if (!basket) { - return null - } - - const item = basket.items[0] + const item = basket && basket.items[0] if (!item) { - return
No item in basket
+ return ( +
+ No item in basket + {formatErrors(errors)} +
+ ) } const coupon = basket.coupons.find(coupon => @@ -259,7 +282,7 @@ export class CheckoutPage extends React.Component { Apply - {errors ?
Error: {errors}
: null} + {formatErrors(errors)} @@ -302,15 +325,14 @@ const mapStateToProps = state => ({ }) const mapDispatchToProps = dispatch => ({ checkout: () => dispatch(mutateAsync(queries.ecommerce.checkoutMutation())), + fetchBasket: () => dispatch(requestAsync(queries.ecommerce.basketQuery())), updateBasket: payload => dispatch(mutateAsync(queries.ecommerce.basketMutation(payload))) }) -const mapPropsToConfigs = () => [queries.ecommerce.basketQuery()] export default compose( connect( mapStateToProps, mapDispatchToProps - ), - connectRequest(mapPropsToConfigs) + ) )(CheckoutPage) diff --git a/static/js/lib/ecommerce.js b/static/js/lib/ecommerce.js index 87c6271471..3e330f91ef 100644 --- a/static/js/lib/ecommerce.js +++ b/static/js/lib/ecommerce.js @@ -1,4 +1,5 @@ // @flow +import React from "react" import Decimal from "decimal.js-light" import * as R from "ramda" import { equals } from "ramda" @@ -57,6 +58,24 @@ const formatDateForRun = (dateString: ?string) => export const formatRunTitle = (run: CourseRun) => `${formatDateForRun(run.start_date)} - ${formatDateForRun(run.end_date)}` +export const formatErrors = (errors: string | Object) => { + if (!errors) { + return null + } + + let errorString + if (typeof errors === "object") { + if (errors.items) { + errorString = errors.items[0] + } else { + errorString = errors[0] + } + } else { + errorString = errors + } + return
{errorString}
+} + export const isPromo = equals(COUPON_TYPE_PROMO) export const createProductMap = ( diff --git a/yarn.lock b/yarn.lock index 3c09db0678..4ba614640a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7157,12 +7157,13 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -query-string@^6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.4.0.tgz#1566c0cec3a2da2d82c222ed3f9e2a921dba5e6a" - integrity sha512-Werid2I41/tJTqOGPJ3cC3vwrIh/8ZupBQbp7BSsqXzr+pTin3aMJ/EZb8UEuk7ZO3VqQFvq2qck/ihc6wqIdw== +query-string@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.5.0.tgz#2e1a70125af01f6f04573692d02c09302a1d8bfc" + integrity sha512-TYC4hDjZSvVxLMEucDMySkuAS9UIzSbAiYGyA9GWCjLKB8fQpviFbjd20fD7uejCDxZS+ftSdBKE6DS+xucJFg== dependencies: decode-uri-component "^0.2.0" + split-on-first "^1.0.0" strict-uri-encode "^2.0.0" querystring-es3@^0.2.0: @@ -8324,6 +8325,11 @@ spdx-license-ids@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz#81c0ce8f21474756148bbb5f3bfc0f36bf15d76e" +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"