Refactor InboxPage:

use txHasBeenDelivered function,
rename css classes,
add txIsRequested check and handle unknown transition separately,
filter unknown transitions when fetching data,
don't render InboxItem if latest transition is unknown,
update InboxPage tests.
This commit is contained in:
Jenni Nurmi 2019-01-23 13:27:22 +02:00 committed by Vesa Luusua
parent 32fc6ba5b1
commit 6d6ebbffc1
5 changed files with 151 additions and 114 deletions

View file

@ -331,8 +331,25 @@
/* Transaction status affects to certain font colors and weights */
.nameEnquired,
.namePending {
.stateName {
/* This class is empty on purpose, it is used below for banned users */
}
.stateActionNeeded {
font-weight: var(--fontWeightSemiBold);
color: var(--attentionColor);
}
.stateNoActionNeeded {
color: var(--matterColorAnti);
}
.stateSucces {
font-weight: var(--fontWeightSemiBold);
color: var(--successColor);
}
.nameEmphasized {
font-weight: var(--fontWeightBold);
/* baseline alignment fixes */
@ -340,39 +357,11 @@
margin-bottom: 1px;
}
.stateName {
/* This class is empty on purpose, it is used below for banned users */
}
.stateEnquired,
.stateRequested,
.statePending {
font-weight: var(--fontWeightSemiBold);
color: var(--attentionColor);
}
.stateAccepted {
font-weight: var(--fontWeightSemiBold);
color: var(--successColor);
}
.stateCanceled,
.stateDeclined,
.stateDelivered {
color: var(--matterColorAnti);
}
.nameEnquiredOrder,
.nameRequested,
.nameDeclined,
.nameCanceled,
.nameAccepted,
.nameDelivered {
.nameNotEmphasized {
font-weight: var(--fontWeightMedium);
}
.bookingEnquired,
.bookingPending {
.bookingActionNeeded {
color: var(--matterColor);
margin-top: 4px;
@ -381,25 +370,16 @@
}
}
.bookingRequested,
.bookingCanceled,
.bookingDeclined,
.bookingAccepted,
.bookingDelivered {
.bookingNoActionNeeded {
color: var(--matterColorAnti);
}
.lastTransitionedAtEnquired,
.lastTransitionedAtPending,
.lastTransitionedAtRequested {
.lastTransitionedAtEmphasized {
color: var(--matterColor);
font-weight: var(--fontWeightMedium);
}
.lastTransitionedAtCanceled,
.lastTransitionedAtAccepted,
.lastTransitionedAtDeclined,
.lastTransitionedAtDelivered {
.lastTransitionedAtNotEmphasized {
color: var(--matterColorAnti);
}

View file

@ -2,6 +2,7 @@ import reverse from 'lodash/reverse';
import sortBy from 'lodash/sortBy';
import { storableError } from '../../util/errors';
import { parse } from '../../util/urlHelpers';
import { TRANSITIONS } from '../../util/transaction';
import { addMarketplaceEntities } from '../../ducks/marketplaceData.duck';
const sortedTransactions = txs =>
@ -91,6 +92,7 @@ export const loadData = (params, search) => (dispatch, getState, sdk) => {
const apiQueryParams = {
only: onlyFilter,
lastTransitions: TRANSITIONS,
include: ['provider', 'provider.profileImage', 'customer', 'customer.profileImage', 'booking'],
'fields.image': ['variants.square-small', 'variants.square-small2x'],
page,

View file

@ -6,14 +6,12 @@ import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import moment from 'moment';
import classNames from 'classnames';
import {
txHasFirstReview,
txIsAccepted,
txIsCanceled,
txIsCompleted,
txIsDeclinedOrExpired,
txIsEnquired,
txIsRequested,
txIsReviewed,
txHasBeenDelivered,
} from '../../util/transaction';
import { LINE_ITEM_DAY, LINE_ITEM_UNITS, propTypes } from '../../util/types';
import { formatMoney } from '../../util/currency';
@ -55,73 +53,85 @@ const formatDate = (intl, date) => {
};
// Translated name of the state of the given transaction
const txState = (intl, tx, isOrder) => {
if (txIsAccepted(tx)) {
export const txState = (intl, tx, type) => {
const isOrder = type === 'order';
if (txIsEnquired(tx)) {
return {
nameClassName: css.nameAccepted,
bookingClassName: css.bookingAccepted,
lastTransitionedAtClassName: css.lastTransitionedAtAccepted,
stateClassName: css.stateAccepted,
state: intl.formatMessage({
id: 'InboxPage.stateAccepted',
}),
};
} else if (txIsDeclinedOrExpired(tx)) {
return {
nameClassName: css.nameDeclined,
bookingClassName: css.bookingDeclined,
lastTransitionedAtClassName: css.lastTransitionedAtDeclined,
stateClassName: css.stateDeclined,
state: intl.formatMessage({
id: 'InboxPage.stateDeclined',
}),
};
} else if (txIsCanceled(tx)) {
return {
nameClassName: css.nameCanceled,
bookingClassName: css.bookingCanceled,
lastTransitionedAtClassName: css.lastTransitionedAtCanceled,
stateClassName: css.stateCanceled,
state: intl.formatMessage({
id: 'InboxPage.stateCanceled',
}),
};
} else if (txIsCompleted(tx) || txHasFirstReview(tx) || txIsReviewed(tx)) {
return {
nameClassName: css.nameDelivered,
bookingClassName: css.bookingDelivered,
lastTransitionedAtClassName: css.lastTransitionedAtDelivered,
stateClassName: css.stateDelivered,
state: intl.formatMessage({
id: 'InboxPage.stateDelivered',
}),
};
} else if (txIsEnquired(tx)) {
return {
nameClassName: isOrder ? css.nameEnquiredOrder : css.nameEnquired,
bookingClassName: css.bookingEnquired,
lastTransitionedAtClassName: css.lastTransitionedAtEnquired,
stateClassName: css.stateEnquired,
nameClassName: isOrder ? css.nameNotEmphasized : css.nameEmphasized,
bookingClassName: css.bookingActionNeeded,
lastTransitionedAtClassName: css.lastTransitionedAtEmphasized,
stateClassName: css.stateActionNeeded,
state: intl.formatMessage({
id: 'InboxPage.stateEnquiry',
}),
};
} else if (txIsRequested(tx)) {
const requested = isOrder
? {
nameClassName: css.nameNotEmphasized,
bookingClassName: css.bookingNoActionNeeded,
lastTransitionedAtClassName: css.lastTransitionedAtEmphasized,
stateClassName: css.stateActionNeeded,
state: intl.formatMessage({
id: 'InboxPage.stateRequested',
}),
}
: {
nameClassName: css.nameEmphasized,
bookingClassName: css.bookingActionNeeded,
lastTransitionedAtClassName: css.lastTransitionedAtEmphasized,
stateClassName: css.stateActionNeeded,
state: intl.formatMessage({
id: 'InboxPage.statePending',
}),
};
return requested;
} else if (txIsDeclinedOrExpired(tx)) {
return {
nameClassName: css.nameNotEmphasized,
bookingClassName: css.bookingNoActionNeeded,
lastTransitionedAtClassName: css.lastTransitionedAtNotEmphasized,
stateClassName: css.stateNoActionNeeded,
state: intl.formatMessage({
id: 'InboxPage.stateDeclined',
}),
};
} else if (txIsAccepted(tx)) {
return {
nameClassName: css.nameNotEmphasized,
bookingClassName: css.bookingNoActionNeeded,
lastTransitionedAtClassName: css.lastTransitionedAtNotEmphasized,
stateClassName: css.stateSucces,
state: intl.formatMessage({
id: 'InboxPage.stateAccepted',
}),
};
} else if (txIsCanceled(tx)) {
return {
nameClassName: css.nameNotEmphasized,
bookingClassName: css.bookingNoActionNeeded,
lastTransitionedAtClassName: css.lastTransitionedAtNotEmphasized,
stateClassName: css.stateNoActionNeeded,
state: intl.formatMessage({
id: 'InboxPage.stateCanceled',
}),
};
} else if (txHasBeenDelivered(tx)) {
return {
nameClassName: css.nameNotEmphasized,
bookingClassName: css.bookingNoActionNeeded,
lastTransitionedAtClassName: css.lastTransitionedAtNotEmphasized,
stateClassName: css.stateNoActionNeeded,
state: intl.formatMessage({
id: 'InboxPage.stateDelivered',
}),
};
} else {
console.warn('This transition is unknown:', tx.attributes.lastTransition);
return null;
}
return {
nameClassName: isOrder ? css.nameRequested : css.namePending,
bookingClassName: isOrder ? css.bookingRequested : css.bookingPending,
lastTransitionedAtClassName: isOrder
? css.lastTransitionedAtRequested
: css.lastTransitionedAtPending,
stateClassName: isOrder ? css.stateRequested : css.statePending,
state: isOrder
? intl.formatMessage({
id: 'InboxPage.stateRequested',
})
: intl.formatMessage({
id: 'InboxPage.statePending',
}),
};
};
const bookingData = (unitType, tx, isOrder, intl) => {
@ -174,7 +184,7 @@ BookingInfoMaybe.propTypes = {
};
export const InboxItem = props => {
const { unitType, type, tx, intl } = props;
const { unitType, type, tx, intl, stateData } = props;
const { customer, provider } = tx;
const isOrder = type === 'order';
@ -185,7 +195,6 @@ export const InboxItem = props => {
const isOtherUserBanned = otherUser.attributes.banned;
const otherUserDisplayName = userDisplayName(otherUser, bannedUserDisplayName);
const stateData = txState(intl, tx, isOrder);
const isSaleNotification = !isOrder && txIsRequested(tx);
const rowNotificationDot = isSaleNotification ? <div className={css.notificationDot} /> : null;
const lastTransitionedAt = formatDate(intl, tx.attributes.lastTransitionedAt);
@ -268,11 +277,15 @@ export const InboxPageComponent = props => {
const title = isOrders ? ordersTitle : salesTitle;
const toTxItem = tx => {
return (
const type = isOrders ? 'order' : 'sale';
const stateData = txState(intl, tx, type);
// Render InboxItem only if the latest transition of the transaction is handled in the `txState` function.
return stateData ? (
<li key={tx.id.uuid} className={css.listItem}>
<InboxItem unitType={unitType} type={isOrders ? 'order' : 'sale'} tx={tx} intl={intl} />
<InboxItem unitType={unitType} type={type} tx={tx} intl={intl} stateData={stateData} />
</li>
);
) : null;
};
const error = fetchOrdersOrSalesError ? (

View file

@ -8,7 +8,7 @@ import {
createTransaction,
createBooking,
} from '../../util/test-data';
import { InboxPageComponent, InboxItem } from './InboxPage';
import { InboxPageComponent, InboxItem, txState } from './InboxPage';
import routeConfiguration from '../../routeConfiguration';
import { TRANSITION_REQUEST } from '../../util/transaction';
import { LINE_ITEM_NIGHT } from '../../util/types';
@ -74,6 +74,8 @@ describe('InboxPage', () => {
const ordersTree = renderShallow(<InboxPageComponent {...ordersProps} />);
expect(ordersTree).toMatchSnapshot();
const stateDataOrder = txState(fakeIntl, ordersProps.transactions[0], 'order');
// Deeply render one InboxItem
const orderItem = renderDeep(
<InboxItem
@ -81,6 +83,7 @@ describe('InboxPage', () => {
type="order"
tx={ordersProps.transactions[0]}
intl={fakeIntl}
stateData={stateDataOrder}
/>
);
expect(orderItem).toMatchSnapshot();
@ -128,6 +131,8 @@ describe('InboxPage', () => {
const salesTree = renderShallow(<InboxPageComponent {...salesProps} />);
expect(salesTree).toMatchSnapshot();
const stateDataSale = txState(fakeIntl, salesProps.transactions[0], 'sale');
// Deeply render one InboxItem
const saleItem = renderDeep(
<InboxItem
@ -135,6 +140,7 @@ describe('InboxPage', () => {
type="sale"
tx={salesProps.transactions[0]}
intl={fakeIntl}
stateData={stateDataSale}
/>
);
expect(saleItem).toMatchSnapshot();

View file

@ -91,6 +91,15 @@ exports[`InboxPage matches snapshot 1`] = `
"now": [Function],
}
}
stateData={
Object {
"bookingClassName": undefined,
"lastTransitionedAtClassName": undefined,
"nameClassName": undefined,
"state": "InboxPage.stateRequested",
"stateClassName": undefined,
}
}
tx={
Object {
"attributes": Object {
@ -216,6 +225,15 @@ exports[`InboxPage matches snapshot 1`] = `
"now": [Function],
}
}
stateData={
Object {
"bookingClassName": undefined,
"lastTransitionedAtClassName": undefined,
"nameClassName": undefined,
"state": "InboxPage.stateRequested",
"stateClassName": undefined,
}
}
tx={
Object {
"attributes": Object {
@ -483,6 +501,15 @@ exports[`InboxPage matches snapshot 3`] = `
"now": [Function],
}
}
stateData={
Object {
"bookingClassName": undefined,
"lastTransitionedAtClassName": undefined,
"nameClassName": undefined,
"state": "InboxPage.statePending",
"stateClassName": undefined,
}
}
tx={
Object {
"attributes": Object {
@ -608,6 +635,15 @@ exports[`InboxPage matches snapshot 3`] = `
"now": [Function],
}
}
stateData={
Object {
"bookingClassName": undefined,
"lastTransitionedAtClassName": undefined,
"nameClassName": undefined,
"state": "InboxPage.statePending",
"stateClassName": undefined,
}
}
tx={
Object {
"attributes": Object {