mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 12:43:11 +10:00
314 lines
11 KiB
JavaScript
314 lines
11 KiB
JavaScript
import { ensureTransaction } from './data';
|
|
|
|
/**
|
|
* Transitions
|
|
*
|
|
* These strings must sync with values defined in Flex API,
|
|
* since transaction objects given by API contain info about last transitions.
|
|
* All the actions in API side happen in transitions,
|
|
* so we need to understand what those strings mean.
|
|
*/
|
|
|
|
// When a customer makes a booking to a listing, a transaction is
|
|
// created with the initial request transition.
|
|
export const TRANSITION_REQUEST = 'transition/request';
|
|
|
|
// A customer can also initiate a transaction with an enquiry, and
|
|
// then transition that with a request.
|
|
export const TRANSITION_ENQUIRE = 'transition/enquire';
|
|
export const TRANSITION_REQUEST_AFTER_ENQUIRY = 'transition/request-after-enquiry';
|
|
|
|
// When the provider accepts or declines a transaction from the
|
|
// SalePage, it is transitioned with the accept or decline transition.
|
|
export const TRANSITION_ACCEPT = 'transition/accept';
|
|
export const TRANSITION_DECLINE = 'transition/decline';
|
|
|
|
// The backend automatically expire the transaction.
|
|
export const TRANSITION_EXPIRE = 'transition/expire';
|
|
|
|
// Admin can also cancel the transition.
|
|
export const TRANSITION_CANCEL = 'transition/cancel';
|
|
|
|
// The backend will mark the transaction completed.
|
|
export const TRANSITION_COMPLETE = 'transition/complete';
|
|
|
|
// Reviews are given through transaction transitions. Review 1 can be
|
|
// by provider or customer, and review 2 will be the other party of
|
|
// the transaction.
|
|
export const TRANSITION_REVIEW_1_BY_PROVIDER = 'transition/review-1-by-provider';
|
|
export const TRANSITION_REVIEW_2_BY_PROVIDER = 'transition/review-2-by-provider';
|
|
export const TRANSITION_REVIEW_1_BY_CUSTOMER = 'transition/review-1-by-customer';
|
|
export const TRANSITION_REVIEW_2_BY_CUSTOMER = 'transition/review-2-by-customer';
|
|
export const TRANSITION_EXPIRE_CUSTOMER_REVIEW_PERIOD = 'transition/expire-customer-review-period';
|
|
export const TRANSITION_EXPIRE_PROVIDER_REVIEW_PERIOD = 'transition/expire-provider-review-period';
|
|
export const TRANSITION_EXPIRE_REVIEW_PERIOD = 'transition/expire-review-period';
|
|
|
|
/**
|
|
* Actors
|
|
*
|
|
* There are 4 different actors that might initiate transitions:
|
|
*/
|
|
|
|
// Roles of actors that perform transaction transitions
|
|
export const TX_TRANSITION_ACTOR_CUSTOMER = 'customer';
|
|
export const TX_TRANSITION_ACTOR_PROVIDER = 'provider';
|
|
export const TX_TRANSITION_ACTOR_SYSTEM = 'system';
|
|
export const TX_TRANSITION_ACTOR_OPERATOR = 'operator';
|
|
|
|
export const TX_TRANSITION_ACTORS = [
|
|
TX_TRANSITION_ACTOR_CUSTOMER,
|
|
TX_TRANSITION_ACTOR_PROVIDER,
|
|
TX_TRANSITION_ACTOR_SYSTEM,
|
|
TX_TRANSITION_ACTOR_OPERATOR,
|
|
];
|
|
|
|
/**
|
|
* States
|
|
*
|
|
* These constants are only for making it clear how transitions work together.
|
|
* You should not use these constants outside of this file.
|
|
*
|
|
* Note: these states are not in sync with states used transaction process definitions
|
|
* in Marketplace API. Only last transitions are passed along transaction object.
|
|
*/
|
|
const STATE_INITIAL = 'initial';
|
|
const STATE_ENQUIRY = 'enquiry';
|
|
const STATE_PREAUTHORIZED = 'preauthorized';
|
|
const STATE_DECLINED = 'declined';
|
|
const STATE_ACCEPTED = 'accepted';
|
|
const STATE_CANCELED = 'canceled';
|
|
const STATE_DELIVERED = 'delivered';
|
|
const STATE_REVIEWED = 'reviewed';
|
|
const STATE_REVIEWED_BY_CUSTOMER = 'reviewed-by-customer';
|
|
const STATE_REVIEWED_BY_PROVIDER = 'reviewed-by-provider';
|
|
|
|
/**
|
|
* Description of transaction process
|
|
*
|
|
* You should keep this in sync with transaction process defined in Marketplace API
|
|
*
|
|
* Note: we don't use yet any state machine library,
|
|
* but this description format is following Xstate (FSM library)
|
|
* https://xstate.js.org/docs/
|
|
*/
|
|
const stateDescription = {
|
|
// id is defined only to support Xstate format.
|
|
// However if you have multiple transaction processes defined,
|
|
// it is best to keep them in sync with transaction process aliases.
|
|
id: 'preauth-with-nightly-booking/release-1',
|
|
|
|
// This 'initial' state is a starting point for new transaction
|
|
initial: STATE_INITIAL,
|
|
|
|
// States
|
|
states: {
|
|
[STATE_INITIAL]: {
|
|
on: {
|
|
[TRANSITION_ENQUIRE]: STATE_ENQUIRY,
|
|
[TRANSITION_REQUEST]: STATE_PREAUTHORIZED,
|
|
},
|
|
},
|
|
[STATE_ENQUIRY]: {
|
|
on: {
|
|
[TRANSITION_REQUEST_AFTER_ENQUIRY]: STATE_PREAUTHORIZED,
|
|
},
|
|
},
|
|
|
|
[STATE_PREAUTHORIZED]: {
|
|
on: {
|
|
[TRANSITION_DECLINE]: STATE_DECLINED,
|
|
[TRANSITION_EXPIRE]: STATE_DECLINED,
|
|
[TRANSITION_ACCEPT]: STATE_ACCEPTED,
|
|
},
|
|
},
|
|
|
|
[STATE_DECLINED]: {},
|
|
[STATE_ACCEPTED]: {
|
|
on: {
|
|
[TRANSITION_CANCEL]: STATE_CANCELED,
|
|
[TRANSITION_COMPLETE]: STATE_DELIVERED,
|
|
},
|
|
},
|
|
|
|
[STATE_CANCELED]: {},
|
|
[STATE_DELIVERED]: {
|
|
on: {
|
|
[TRANSITION_EXPIRE_REVIEW_PERIOD]: STATE_REVIEWED,
|
|
[TRANSITION_REVIEW_1_BY_CUSTOMER]: STATE_REVIEWED_BY_CUSTOMER,
|
|
[TRANSITION_REVIEW_1_BY_PROVIDER]: STATE_REVIEWED_BY_PROVIDER,
|
|
},
|
|
},
|
|
|
|
[STATE_REVIEWED_BY_CUSTOMER]: {
|
|
on: {
|
|
[TRANSITION_REVIEW_2_BY_PROVIDER]: STATE_REVIEWED,
|
|
[TRANSITION_EXPIRE_PROVIDER_REVIEW_PERIOD]: STATE_REVIEWED,
|
|
},
|
|
},
|
|
[STATE_REVIEWED_BY_PROVIDER]: {
|
|
on: {
|
|
[TRANSITION_REVIEW_2_BY_CUSTOMER]: STATE_REVIEWED,
|
|
[TRANSITION_EXPIRE_CUSTOMER_REVIEW_PERIOD]: STATE_REVIEWED,
|
|
},
|
|
},
|
|
[STATE_REVIEWED]: { type: 'final' },
|
|
},
|
|
};
|
|
|
|
// Note: currently we assume that state description doesn't contain nested states.
|
|
const statesFromStateDescription = description => description.states || {};
|
|
|
|
// Get all the transitions from states object in an array
|
|
const getTransitions = states => {
|
|
const stateNames = Object.keys(states);
|
|
|
|
const transitionsReducer = (transitionArray, name) => {
|
|
const stateTransitions = states[name] && states[name].on;
|
|
const transitionKeys = stateTransitions ? Object.keys(stateTransitions) : [];
|
|
return [
|
|
...transitionArray,
|
|
...transitionKeys.map(key => ({ key, value: stateTransitions[key] })),
|
|
];
|
|
};
|
|
|
|
return stateNames.reduce(transitionsReducer, []);
|
|
};
|
|
|
|
// This is a list of all the transitions that this app should be able to handle.
|
|
export const TRANSITIONS = getTransitions(statesFromStateDescription(stateDescription)).map(
|
|
t => t.key
|
|
);
|
|
|
|
// This function returns a function that has given stateDesc in scope chain.
|
|
const getTransitionsToStateFn = stateDesc => state =>
|
|
getTransitions(statesFromStateDescription(stateDesc))
|
|
.filter(t => t.value === state)
|
|
.map(t => t.key);
|
|
|
|
// Get all the transitions that lead to specified state.
|
|
const getTransitionsToState = getTransitionsToStateFn(stateDescription);
|
|
|
|
// This is needed to fetch transactions that need response from provider.
|
|
// I.e. transactions which provider needs to accept or decline
|
|
export const transitionsToRequested = getTransitionsToState(STATE_PREAUTHORIZED);
|
|
|
|
/**
|
|
* Helper functions to figure out if transaction is in a specific state.
|
|
* State is based on lastTransition given by transaction object and state description.
|
|
*/
|
|
|
|
const txLastTransition = tx => ensureTransaction(tx).attributes.lastTransition;
|
|
|
|
// DEPRECATED: use txIsDelivered instead
|
|
export const txIsCompleted = tx => txLastTransition(tx) === TRANSITION_COMPLETE;
|
|
|
|
export const txIsEnquired = tx =>
|
|
getTransitionsToState(STATE_ENQUIRY).includes(txLastTransition(tx));
|
|
|
|
// Note: state name used in Marketplace API docs (and here) is actually preauthorized
|
|
// However, word "requested" is used in many places so that we decided to keep it.
|
|
export const txIsRequested = tx =>
|
|
getTransitionsToState(STATE_PREAUTHORIZED).includes(txLastTransition(tx));
|
|
|
|
export const txIsAccepted = tx =>
|
|
getTransitionsToState(STATE_ACCEPTED).includes(txLastTransition(tx));
|
|
|
|
export const txIsDeclined = tx =>
|
|
getTransitionsToState(STATE_DECLINED).includes(txLastTransition(tx));
|
|
|
|
export const txIsCanceled = tx =>
|
|
getTransitionsToState(STATE_CANCELED).includes(txLastTransition(tx));
|
|
|
|
export const txIsDelivered = tx =>
|
|
getTransitionsToState(STATE_DELIVERED).includes(txLastTransition(tx));
|
|
|
|
const firstReviewTransitions = [
|
|
...getTransitionsToState(STATE_REVIEWED_BY_CUSTOMER),
|
|
...getTransitionsToState(STATE_REVIEWED_BY_PROVIDER),
|
|
];
|
|
export const txIsInFirstReview = tx => firstReviewTransitions.includes(txLastTransition(tx));
|
|
|
|
export const txIsInFirstReviewBy = (tx, isCustomer) =>
|
|
isCustomer
|
|
? getTransitionsToState(STATE_REVIEWED_BY_CUSTOMER).includes(txLastTransition(tx))
|
|
: getTransitionsToState(STATE_REVIEWED_BY_PROVIDER).includes(txLastTransition(tx));
|
|
|
|
export const txIsReviewed = tx =>
|
|
getTransitionsToState(STATE_REVIEWED).includes(txLastTransition(tx));
|
|
|
|
/**
|
|
* Helper functions to figure out if transaction has passed a given state.
|
|
* This is based on transitions history given by transaction object.
|
|
*/
|
|
|
|
const txTransitions = tx => ensureTransaction(tx).attributes.transitions || [];
|
|
const hasPassedTransition = (transitionName, tx) =>
|
|
!!txTransitions(tx).find(t => t.transition === transitionName);
|
|
|
|
const hasPassedStateFn = state => tx =>
|
|
getTransitionsToState(state).filter(t => hasPassedTransition(t, tx)).length > 0;
|
|
|
|
export const txHasBeenAccepted = hasPassedStateFn(STATE_ACCEPTED);
|
|
export const txHasBeenDelivered = hasPassedStateFn(STATE_DELIVERED);
|
|
|
|
/**
|
|
* Other transaction related utility functions
|
|
*/
|
|
|
|
export const transitionIsReviewed = transition =>
|
|
getTransitionsToState(STATE_REVIEWED).includes(transition);
|
|
|
|
export const transitionIsFirstReviewedBy = (transition, isCustomer) =>
|
|
isCustomer
|
|
? getTransitionsToState(STATE_REVIEWED_BY_CUSTOMER).includes(transition)
|
|
: getTransitionsToState(STATE_REVIEWED_BY_PROVIDER).includes(transition);
|
|
|
|
export const getReview1Transition = isCustomer =>
|
|
isCustomer ? TRANSITION_REVIEW_1_BY_CUSTOMER : TRANSITION_REVIEW_1_BY_PROVIDER;
|
|
|
|
export const getReview2Transition = isCustomer =>
|
|
isCustomer ? TRANSITION_REVIEW_2_BY_CUSTOMER : TRANSITION_REVIEW_2_BY_PROVIDER;
|
|
|
|
// Check if a transition is the kind that should be rendered
|
|
// when showing transition history (e.g. ActivityFeed)
|
|
// The first transition and most of the expiration transitions made by system are not relevant
|
|
export const isRelevantPastTransition = transition => {
|
|
return [
|
|
TRANSITION_ACCEPT,
|
|
TRANSITION_CANCEL,
|
|
TRANSITION_COMPLETE,
|
|
TRANSITION_DECLINE,
|
|
TRANSITION_EXPIRE,
|
|
TRANSITION_REQUEST,
|
|
TRANSITION_REQUEST_AFTER_ENQUIRY,
|
|
TRANSITION_REVIEW_1_BY_CUSTOMER,
|
|
TRANSITION_REVIEW_1_BY_PROVIDER,
|
|
TRANSITION_REVIEW_2_BY_CUSTOMER,
|
|
TRANSITION_REVIEW_2_BY_PROVIDER,
|
|
].includes(transition);
|
|
};
|
|
|
|
export const isCustomerReview = transition => {
|
|
return [TRANSITION_REVIEW_1_BY_CUSTOMER, TRANSITION_REVIEW_2_BY_CUSTOMER].includes(transition);
|
|
};
|
|
|
|
export const isProviderReview = transition => {
|
|
return [TRANSITION_REVIEW_1_BY_PROVIDER, TRANSITION_REVIEW_2_BY_PROVIDER].includes(transition);
|
|
};
|
|
|
|
export const getUserTxRole = (currentUserId, transaction) => {
|
|
const tx = ensureTransaction(transaction);
|
|
const customer = tx.customer;
|
|
if (currentUserId && currentUserId.uuid && tx.id && customer.id) {
|
|
// user can be either customer or provider
|
|
return currentUserId.uuid === customer.id.uuid
|
|
? TX_TRANSITION_ACTOR_CUSTOMER
|
|
: TX_TRANSITION_ACTOR_PROVIDER;
|
|
} else {
|
|
throw new Error(`Parameters for "userIsCustomer" function were wrong.
|
|
currentUserId: ${currentUserId}, transaction: ${transaction}`);
|
|
}
|
|
};
|
|
|
|
export const txRoleIsProvider = userRole => userRole === TX_TRANSITION_ACTOR_PROVIDER;
|
|
export const txRoleIsCustomer = userRole => userRole === TX_TRANSITION_ACTOR_CUSTOMER;
|