From 08fcfac609fba703c9b67e76add4a04a6c09120f Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Fri, 9 Mar 2018 13:43:14 +0200 Subject: [PATCH] BookingBreakdown can handle customer commission --- .../BookingBreakdown/BookingBreakdown.css | 17 ++++++ .../BookingBreakdown/BookingBreakdown.js | 36 +++++++++-- .../LineItemCustomerCommissionMaybe.js | 60 +++++++++++++++++++ ....js => LineItemProviderCommissionMaybe.js} | 6 +- .../BookingBreakdown/LineItemSubTotalMaybe.js | 33 ++++++++-- .../BookingBreakdown.test.js.snap | 14 +++++ src/translations/en.json | 3 +- src/util/types.js | 9 ++- 8 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 src/components/BookingBreakdown/LineItemCustomerCommissionMaybe.js rename src/components/BookingBreakdown/{LineItemCommissionMaybe.js => LineItemProviderCommissionMaybe.js} (93%) diff --git a/src/components/BookingBreakdown/BookingBreakdown.css b/src/components/BookingBreakdown/BookingBreakdown.css index 33e6fbc3..7ff45a13 100644 --- a/src/components/BookingBreakdown/BookingBreakdown.css +++ b/src/components/BookingBreakdown/BookingBreakdown.css @@ -82,3 +82,20 @@ margin: 2px 0 0 10px; } } + +.feeInfo { + @apply --marketplaceTinyFontStyles; + color: var(--matterColorAnti); + margin: 0 0 20px 0; + flex-shrink: 0; + + @media (--viewportMedium) { + margin-top: auto; + margin-bottom: 20px; + } + + @media (--viewportLarge) { + margin-top: 4px; + margin-bottom: 22px; + } +} diff --git a/src/components/BookingBreakdown/BookingBreakdown.js b/src/components/BookingBreakdown/BookingBreakdown.js index 0913a1af..6c487ee4 100644 --- a/src/components/BookingBreakdown/BookingBreakdown.js +++ b/src/components/BookingBreakdown/BookingBreakdown.js @@ -4,15 +4,20 @@ */ import React from 'react'; import { oneOf, string } from 'prop-types'; -import { intlShape, injectIntl } from 'react-intl'; +import { FormattedMessage, intlShape, injectIntl } from 'react-intl'; import classNames from 'classnames'; -import { propTypes } from '../../util/types'; +import { + propTypes, + LINE_ITEM_CUSTOMER_COMMISSION, + LINE_ITEM_PROVIDER_COMMISSION, +} from '../../util/types'; import LineItemUnitPrice from './LineItemUnitPrice'; import LineItemBookingPeriod from './LineItemBookingPeriod'; import LineItemUnitsMaybe from './LineItemUnitsMaybe'; import LineItemSubTotalMaybe from './LineItemSubTotalMaybe'; -import LineItemCommissionMaybe from './LineItemCommissionMaybe'; +import LineItemCustomerCommissionMaybe from './LineItemCustomerCommissionMaybe'; +import LineItemProviderCommissionMaybe from './LineItemProviderCommissionMaybe'; import LineItemRefundMaybe from './LineItemRefundMaybe'; import LineItemTotalPrice from './LineItemTotalPrice'; import css from './BookingBreakdown.css'; @@ -20,7 +25,16 @@ import css from './BookingBreakdown.css'; export const BookingBreakdownComponent = props => { const { rootClassName, className, userRole, unitType, transaction, booking, intl } = props; + const isCustomer = userRole === 'customer'; const isProvider = userRole === 'provider'; + + const commissionLineItem = transaction.attributes.lineItems.find( + item => + (item.code === LINE_ITEM_CUSTOMER_COMMISSION || + item.code === LINE_ITEM_PROVIDER_COMMISSION) && + !item.reversal + ); + const classes = classNames(rootClassName || css.root, className); return ( @@ -32,14 +46,28 @@ export const BookingBreakdownComponent = props => { + + -
+ {commissionLineItem ? ( + + + + ) : null} ); }; diff --git a/src/components/BookingBreakdown/LineItemCustomerCommissionMaybe.js b/src/components/BookingBreakdown/LineItemCustomerCommissionMaybe.js new file mode 100644 index 00000000..522f53e3 --- /dev/null +++ b/src/components/BookingBreakdown/LineItemCustomerCommissionMaybe.js @@ -0,0 +1,60 @@ +import React from 'react'; +import { bool } from 'prop-types'; +import { FormattedMessage, intlShape } from 'react-intl'; +import { formatMoney } from '../../util/currency'; +import { types as sdkTypes } from '../../util/sdkLoader'; +import { LINE_ITEM_CUSTOMER_COMMISSION, propTypes } from '../../util/types'; + +import css from './BookingBreakdown.css'; + +const { Money } = sdkTypes; + +// Validate the assumption that the commission exists and the amount +// is zero or positive. +const isValidCommission = commissionLineItem => { + return commissionLineItem.lineTotal instanceof Money && commissionLineItem.lineTotal.amount >= 0; +}; + +const LineItemStudioTimeCustomerCommissionMaybe = props => { + const { transaction, isCustomer, intl } = props; + + const customerCommissionLineItem = transaction.attributes.lineItems.find( + item => item.code === LINE_ITEM_CUSTOMER_COMMISSION && !item.reversal + ); + const commissionRefund = transaction.attributes.lineItems.find( + item => item.code === LINE_ITEM_CUSTOMER_COMMISSION && item.reversal + ); + + // If commission is passed it will be shown as a fee already reduces from the total price + let commissionItem = null; + + if (isCustomer && customerCommissionLineItem) { + if (!isValidCommission(customerCommissionLineItem)) { + // eslint-disable-next-line no-console + console.error('invalid commission line item:', customerCommissionLineItem); + throw new Error('Commission should be present and the value should be zero or positive'); + } + + const commission = customerCommissionLineItem.lineTotal; + const formattedCommission = commission ? formatMoney(intl, commission) : null; + + commissionItem = commissionRefund ? null : ( +
+ + + + {formattedCommission} +
+ ); + } + + return commissionItem; +}; + +LineItemStudioTimeCustomerCommissionMaybe.propTypes = { + transaction: propTypes.transaction.isRequired, + isCustomer: bool.isRequired, + intl: intlShape.isRequired, +}; + +export default LineItemStudioTimeCustomerCommissionMaybe; diff --git a/src/components/BookingBreakdown/LineItemCommissionMaybe.js b/src/components/BookingBreakdown/LineItemProviderCommissionMaybe.js similarity index 93% rename from src/components/BookingBreakdown/LineItemCommissionMaybe.js rename to src/components/BookingBreakdown/LineItemProviderCommissionMaybe.js index 67d373b0..b3da2052 100644 --- a/src/components/BookingBreakdown/LineItemCommissionMaybe.js +++ b/src/components/BookingBreakdown/LineItemProviderCommissionMaybe.js @@ -19,7 +19,7 @@ const isValidCommission = commissionLineItem => { ); }; -const LineItemCommissionMaybe = props => { +const LineItemProviderCommissionMaybe = props => { const { transaction, isProvider, intl } = props; const providerCommissionLineItem = transaction.attributes.lineItems.find( @@ -55,10 +55,10 @@ const LineItemCommissionMaybe = props => { return commissionItem; }; -LineItemCommissionMaybe.propTypes = { +LineItemProviderCommissionMaybe.propTypes = { transaction: propTypes.transaction.isRequired, isProvider: bool.isRequired, intl: intlShape.isRequired, }; -export default LineItemCommissionMaybe; +export default LineItemProviderCommissionMaybe; diff --git a/src/components/BookingBreakdown/LineItemSubTotalMaybe.js b/src/components/BookingBreakdown/LineItemSubTotalMaybe.js index 87ab9715..284e2650 100644 --- a/src/components/BookingBreakdown/LineItemSubTotalMaybe.js +++ b/src/components/BookingBreakdown/LineItemSubTotalMaybe.js @@ -1,13 +1,36 @@ import React from 'react'; -import { bool } from 'prop-types'; +import { string } from 'prop-types'; import { FormattedMessage, intlShape } from 'react-intl'; import { formatMoney } from '../../util/currency'; -import { propTypes } from '../../util/types'; +import { + propTypes, + LINE_ITEM_CUSTOMER_COMMISSION, + LINE_ITEM_PROVIDER_COMMISSION, +} from '../../util/types'; import css from './BookingBreakdown.css'; +/** + * Checks if a transaction has a commission line-item for + * a given user role. + */ +const txHasCommission = (transaction, userRole) => { + let commissionLineItem = null; + + if (userRole === 'customer') { + commissionLineItem = transaction.attributes.lineItems.find( + item => item.code === LINE_ITEM_CUSTOMER_COMMISSION + ); + } else if (userRole === 'provider') { + commissionLineItem = transaction.attributes.lineItems.find( + item => item.code === LINE_ITEM_PROVIDER_COMMISSION + ); + } + return !!commissionLineItem; +}; + const LineItemSubTotalMaybe = props => { - const { transaction, unitType, isProvider, intl } = props; + const { transaction, unitType, userRole, intl } = props; const refund = transaction.attributes.lineItems.find( item => item.code === unitType && item.reversal @@ -16,7 +39,7 @@ const LineItemSubTotalMaybe = props => { // Show unit purchase line total (unit price * quantity) as a subtotal. // PLEASE NOTE that this assumes that the transaction doesn't have other // line item types than the defined unit type (e.g. week, month, year). - const showSubTotal = isProvider || refund; + const showSubTotal = txHasCommission(transaction, userRole) || refund; const unitPurchase = transaction.attributes.lineItems.find( item => item.code === unitType && !item.reversal ); @@ -34,7 +57,7 @@ const LineItemSubTotalMaybe = props => { LineItemSubTotalMaybe.propTypes = { transaction: propTypes.transaction.isRequired, - isProvider: bool.isRequired, + userRole: string.isRequired, intl: intlShape.isRequired, }; diff --git a/src/components/BookingBreakdown/__snapshots__/BookingBreakdown.test.js.snap b/src/components/BookingBreakdown/__snapshots__/BookingBreakdown.test.js.snap index 2af3179a..45df5d77 100644 --- a/src/components/BookingBreakdown/__snapshots__/BookingBreakdown.test.js.snap +++ b/src/components/BookingBreakdown/__snapshots__/BookingBreakdown.test.js.snap @@ -221,6 +221,13 @@ exports[`BookingBreakdown provider canceled transaction data matches snapshot 1` 0 + + + BookingBreakdown.commissionFeeNote + + `; @@ -317,5 +324,12 @@ exports[`BookingBreakdown provider transaction data matches snapshot 1`] = ` 18 + + + BookingBreakdown.commissionFeeNote + + `; diff --git a/src/translations/en.json b/src/translations/en.json index 8437f301..cce0f477 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -38,7 +38,8 @@ "AuthenticationPage.verifyEmailTitle": "{name}, check your inbox to verify your email", "Avatar.bannedUserDisplayName": "Banned user", "BookingBreakdown.bookingPeriod": "{bookingStart} – {bookingEnd}", - "BookingBreakdown.commission": "Saunatime fee", + "BookingBreakdown.commission": "Saunatime fee *", + "BookingBreakdown.commissionFeeNote": "* The fee helps us run the platform and provide the best possible service to you!", "BookingBreakdown.dayCount": "{count, number} {count, plural, one {day} other {days}}", "BookingBreakdown.nightCount": "{count, number} {count, plural, one {night} other {nights}}", "BookingBreakdown.pricePerDay": "Price per day", diff --git a/src/util/types.js b/src/util/types.js index feec3558..92b8cb0d 100644 --- a/src/util/types.js +++ b/src/util/types.js @@ -329,9 +329,16 @@ propTypes.review = shape({ export const LINE_ITEM_NIGHT = 'line-item/night'; export const LINE_ITEM_DAY = 'line-item/day'; export const LINE_ITEM_UNITS = 'line-item/units'; +export const LINE_ITEM_CUSTOMER_COMMISSION = 'line-item/customer-commission'; export const LINE_ITEM_PROVIDER_COMMISSION = 'line-item/provider-commission'; -const LINE_ITEMS = [LINE_ITEM_NIGHT, LINE_ITEM_DAY, LINE_ITEM_UNITS, LINE_ITEM_PROVIDER_COMMISSION]; +const LINE_ITEMS = [ + LINE_ITEM_NIGHT, + LINE_ITEM_DAY, + LINE_ITEM_UNITS, + LINE_ITEM_CUSTOMER_COMMISSION, + LINE_ITEM_PROVIDER_COMMISSION, +]; propTypes.bookingUnitType = oneOf([LINE_ITEM_NIGHT, LINE_ITEM_DAY, LINE_ITEM_UNITS]);