Merge pull request #1131 from sharetribe/ensure-booking-transition-exists

Ensure booking transition exists
This commit is contained in:
Jenni Laakso 2019-07-08 13:47:58 +03:00 committed by GitHub
commit 423a9200cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 7 deletions

View file

@ -14,6 +14,13 @@ way to update this template, but currently, we follow a pattern:
## Upcoming version 2019-XX-XX
## [v3.1.1] 2019-07-08
- [fix] Ensure on `TransactionPanel` that enquiry has a correct transition when a customer tries to
book the listing. This might happen with transaction process changes (e.g. when changing from
previous default to SCA process).
[#1131](https://github.com/sharetribe/flex-template-web/pull/1131)
## [v3.1.0] 2019-07-05
- [fix] SectionHero: fix type in search params. There was an extra "/s?".

View file

@ -1,6 +1,6 @@
{
"name": "app",
"version": "3.1.0",
"version": "3.1.1",
"private": true,
"license": "Apache-2.0",
"dependencies": {

View file

@ -176,7 +176,7 @@ BookingPanel.propTypes = {
onSubmit: func.isRequired,
title: oneOfType([node, string]).isRequired,
subTitle: oneOfType([node, string]),
authorDisplayName: string.isRequired,
authorDisplayName: oneOfType([node, string]).isRequired,
onManageDisableScrolling: func.isRequired,
timeSlots: arrayOf(propTypes.timeSlot),
fetchTimeSlotsError: propTypes.error,

View file

@ -1,8 +1,9 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { array, arrayOf, bool, func, number, string } from 'prop-types';
import { injectIntl, intlShape } from 'react-intl';
import classNames from 'classnames';
import {
TRANSITION_REQUEST_PAYMENT_AFTER_ENQUIRY,
txIsAccepted,
txIsCanceled,
txIsDeclined,
@ -182,6 +183,7 @@ export class TransactionPanelComponent extends Component {
onSubmitBookingRequest,
timeSlots,
fetchTimeSlotsError,
nextTransitions,
} = this.props;
const currentTransaction = ensureTransaction(transaction);
@ -202,9 +204,16 @@ export class TransactionPanelComponent extends Component {
const stateDataFn = tx => {
if (txIsEnquired(tx)) {
const transitions = Array.isArray(nextTransitions)
? nextTransitions.map(transition => {
return transition.attributes.name;
})
: [];
const hasCorrectNextTransition =
transitions.length > 0 && transitions.includes(TRANSITION_REQUEST_PAYMENT_AFTER_ENQUIRY);
return {
headingState: HEADING_ENQUIRED,
showBookingPanel: isCustomer && !isProviderBanned,
showBookingPanel: isCustomer && !isProviderBanned && hasCorrectNextTransition,
};
} else if (txIsPaymentPending(tx)) {
return {
@ -456,10 +465,9 @@ TransactionPanelComponent.defaultProps = {
sendReviewError: null,
timeSlots: null,
fetchTimeSlotsError: null,
nextTransitions: null,
};
const { arrayOf, bool, func, number, string } = PropTypes;
TransactionPanelComponent.propTypes = {
rootClassName: string,
className: string,
@ -483,6 +491,7 @@ TransactionPanelComponent.propTypes = {
onSubmitBookingRequest: func.isRequired,
timeSlots: arrayOf(propTypes.timeSlot),
fetchTimeSlotsError: propTypes.error,
nextTransitions: array,
// Sale related props
onAcceptSale: func.isRequired,

View file

@ -35,6 +35,10 @@ export const FETCH_TRANSACTION_REQUEST = 'app/TransactionPage/FETCH_TRANSACTION_
export const FETCH_TRANSACTION_SUCCESS = 'app/TransactionPage/FETCH_TRANSACTION_SUCCESS';
export const FETCH_TRANSACTION_ERROR = 'app/TransactionPage/FETCH_TRANSACTION_ERROR';
export const FETCH_TRANSITIONS_REQUEST = 'app/TransactionPage/FETCH_TRANSITIONS_REQUEST';
export const FETCH_TRANSITIONS_SUCCESS = 'app/TransactionPage/FETCH_TRANSITIONS_SUCCESS';
export const FETCH_TRANSITIONS_ERROR = 'app/TransactionPage/FETCH_TRANSITIONS_ERROR';
export const ACCEPT_SALE_REQUEST = 'app/TransactionPage/ACCEPT_SALE_REQUEST';
export const ACCEPT_SALE_SUCCESS = 'app/TransactionPage/ACCEPT_SALE_SUCCESS';
export const ACCEPT_SALE_ERROR = 'app/TransactionPage/ACCEPT_SALE_ERROR';
@ -82,6 +86,9 @@ const initialState = {
sendReviewError: null,
timeSlots: null,
fetchTimeSlotsError: null,
fetchTransitionsInProgress: false,
fetchTransitionsError: null,
processTransitions: null,
};
// Merge entity arrays using ids, so that conflicting items in newer array (b) overwrite old values (a).
@ -109,6 +116,14 @@ export default function checkoutPageReducer(state = initialState, action = {}) {
console.error(payload); // eslint-disable-line
return { ...state, fetchTransactionInProgress: false, fetchTransactionError: payload };
case FETCH_TRANSITIONS_REQUEST:
return { ...state, fetchTransitionsInProgress: true, fetchTransitionsError: null };
case FETCH_TRANSITIONS_SUCCESS:
return { ...state, fetchTransitionsInProgress: false, processTransitions: payload };
case FETCH_TRANSITIONS_ERROR:
console.error(payload); // eslint-disable-line
return { ...state, fetchTransitionsInProgress: false, fetchTransitionsError: payload };
case ACCEPT_SALE_REQUEST:
return { ...state, acceptInProgress: true, acceptSaleError: null, declineSaleError: null };
case ACCEPT_SALE_SUCCESS:
@ -192,6 +207,13 @@ const fetchTransactionSuccess = response => ({
});
const fetchTransactionError = e => ({ type: FETCH_TRANSACTION_ERROR, error: true, payload: e });
const fetchTransitionsRequest = () => ({ type: FETCH_TRANSITIONS_REQUEST });
const fetchTransitionsSuccess = response => ({
type: FETCH_TRANSITIONS_SUCCESS,
payload: response,
});
const fetchTransitionsError = e => ({ type: FETCH_TRANSITIONS_ERROR, error: true, payload: e });
const acceptSaleRequest = () => ({ type: ACCEPT_SALE_REQUEST });
const acceptSaleSuccess = () => ({ type: ACCEPT_SALE_SUCCESS });
const acceptSaleError = e => ({ type: ACCEPT_SALE_ERROR, error: true, payload: e });
@ -564,6 +586,19 @@ const fetchTimeSlots = listingId => (dispatch, getState, sdk) => {
});
};
export const fetchNextTransitions = id => (dispatch, getState, sdk) => {
dispatch(fetchTransitionsRequest());
return sdk.processTransitions
.query({ transactionId: id })
.then(res => {
dispatch(fetchTransitionsSuccess(res.data.data));
})
.catch(e => {
dispatch(fetchTransitionsError(storableError(e)));
});
};
// loadData is a collection of async calls that need to be made
// before page has all the info it needs to render itself
export const loadData = params => (dispatch, getState) => {
@ -579,5 +614,9 @@ export const loadData = params => (dispatch, getState) => {
dispatch(setInitialValues(initialValues));
// Sale / order (i.e. transaction entity in API)
return Promise.all([dispatch(fetchTransaction(txId, txRole)), dispatch(fetchMessages(txId, 1))]);
return Promise.all([
dispatch(fetchTransaction(txId, txRole)),
dispatch(fetchMessages(txId, 1)),
dispatch(fetchNextTransitions(txId)),
]);
};

View file

@ -74,6 +74,7 @@ export const TransactionPageComponent = props => {
onDeclineSale,
timeSlots,
fetchTimeSlotsError,
processTransitions,
callSetInitialValues,
onInitializeCardPaymentData,
} = props;
@ -232,6 +233,7 @@ export const TransactionPageComponent = props => {
declineInProgress={declineInProgress}
acceptSaleError={acceptSaleError}
declineSaleError={declineSaleError}
nextTransitions={processTransitions}
onSubmitBookingRequest={handleSubmitBookingRequest}
timeSlots={timeSlots}
fetchTimeSlotsError={fetchTimeSlotsError}
@ -334,6 +336,7 @@ const mapStateToProps = state => {
sendReviewError,
timeSlots,
fetchTimeSlotsError,
processTransitions,
} = state.TransactionPage;
const { currentUser } = state.user;
@ -361,6 +364,7 @@ const mapStateToProps = state => {
sendReviewError,
timeSlots,
fetchTimeSlotsError,
processTransitions,
};
};