Handle banned provider in Order page

This commit is contained in:
Kimmo Puputti 2017-09-15 15:09:33 +03:00
parent a1611fad91
commit 1306fe9413
8 changed files with 82 additions and 29 deletions

View file

@ -76,7 +76,7 @@
}
}
.title {
.heading {
margin: 28px 24px 0 24px;
@media (--viewportMedium) {

View file

@ -1,9 +1,9 @@
import React, { PropTypes } from 'react';
import { FormattedDate, FormattedMessage } from 'react-intl';
import { injectIntl, intlShape, FormattedDate, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import * as propTypes from '../../util/propTypes';
import { createSlug } from '../../util/urlHelpers';
import { ensureListing, ensureTransaction, ensureUser } from '../../util/data';
import { ensureListing, ensureTransaction, ensureUser, userDisplayName } from '../../util/data';
import { BookingBreakdown, NamedLink, ResponsiveImage, AvatarMedium } from '../../components';
import css from './OrderDetailsPanel.css';
@ -110,27 +110,43 @@ const orderMessage = (
}
};
const OrderDetailsPanel = props => {
export const OrderDetailsPanelComponent = props => {
const {
rootClassName,
className,
transaction,
intl,
} = props;
const currentTransaction = ensureTransaction(transaction);
const currentListing = ensureListing(currentTransaction.listing);
const currentProvider = ensureUser(currentTransaction.provider);
const currentCustomer = ensureUser(currentTransaction.customer);
const providerProfile = currentProvider.attributes.profile;
const authorDisplayName = providerProfile.displayName;
const customerDisplayName = currentCustomer.attributes.profile.displayName;
const listingLoaded = !!currentListing.id;
const listingDeleted = listingLoaded && currentListing.attributes.deleted;
const bannedUserDisplayName = intl.formatMessage({
id: 'OrderDetailsPanel.bannedUserDisplayName',
});
const deletedListingTitle = intl.formatMessage({
id: 'OrderDetailsPanel.deletedListingTitle',
});
const deletedListingOrderTitle = intl.formatMessage({
id: 'OrderDetailsPanel.deletedListingOrderTitle',
});
const orderMessageDeletedListing = intl.formatMessage({
id: 'OrderDetailsPanel.messageDeletedListing',
});
const authorDisplayName = userDisplayName(currentProvider, bannedUserDisplayName);
const customerDisplayName = userDisplayName(currentCustomer, bannedUserDisplayName);
const transactionState = currentTransaction.attributes.state;
const lastTransitionedAt = currentTransaction.attributes.lastTransitionedAt;
const lastTransition = currentTransaction.attributes.lastTransitione;
let listingLink = null;
if (currentListing.id && currentListing.attributes.title) {
if (listingLoaded && currentListing.attributes.title) {
const title = currentListing.attributes.title;
const params = { id: currentListing.id.uuid, slug: createSlug(title) };
listingLink = (
@ -138,19 +154,30 @@ const OrderDetailsPanel = props => {
{title}
</NamedLink>
);
} else {
listingLink = deletedListingOrderTitle;
}
const listingTitle = currentListing.attributes.title;
const listingTitle = currentListing.attributes.deleted
? deletedListingTitle
: currentListing.attributes.title;
const bookingInfo = breakdown(currentTransaction);
const title = orderTitle(transactionState, listingLink, customerDisplayName, lastTransition);
const message = orderMessage(
const orderHeading = orderTitle(
transactionState,
listingLink,
authorDisplayName,
lastTransitionedAt,
customerDisplayName,
lastTransition
);
const message = listingDeleted
? orderMessageDeletedListing
: orderMessage(
transactionState,
listingLink,
authorDisplayName,
lastTransitionedAt,
lastTransition
);
const firstImage = currentListing.images && currentListing.images.length > 0
? currentListing.images[0]
@ -177,7 +204,7 @@ const OrderDetailsPanel = props => {
<AvatarMedium user={currentProvider} />
</div>
<div className={css.orderInfo}>
<h1 className={css.title}>{title}</h1>
<h1 className={css.heading}>{orderHeading}</h1>
<div className={css.message}>
{message}
</div>
@ -225,7 +252,7 @@ const OrderDetailsPanel = props => {
);
};
OrderDetailsPanel.defaultProps = {
OrderDetailsPanelComponent.defaultProps = {
rootClassName: null,
className: null,
lastTransition: null,
@ -233,10 +260,15 @@ OrderDetailsPanel.defaultProps = {
const { string } = PropTypes;
OrderDetailsPanel.propTypes = {
OrderDetailsPanelComponent.propTypes = {
rootClassName: string,
className: string,
transaction: propTypes.transaction.isRequired,
// from injectIntl
intl: intlShape,
};
const OrderDetailsPanel = injectIntl(OrderDetailsPanelComponent);
export default OrderDetailsPanel;

View file

@ -3,8 +3,9 @@ import { shallow } from 'enzyme';
import { types } from '../../util/sdkLoader';
import { createBooking, createListing, createUser, createTransaction } from '../../util/test-data';
import { renderShallow } from '../../util/test-helpers';
import { fakeIntl } from '../../util/test-data';
import { BookingBreakdown } from '../../components';
import OrderDetailsPanel from './OrderDetailsPanel.js';
import { OrderDetailsPanelComponent } from './OrderDetailsPanel.js';
describe('OrderDetailsPanel', () => {
it('matches snapshot', () => {
@ -21,7 +22,7 @@ describe('OrderDetailsPanel', () => {
provider: createUser('provider'),
customer: createUser('customer'),
});
const tree = renderShallow(<OrderDetailsPanel transaction={tx} />);
const tree = renderShallow(<OrderDetailsPanelComponent transaction={tx} intl={fakeIntl} />);
expect(tree).toMatchSnapshot();
});
it('renders correct total price', () => {
@ -38,7 +39,7 @@ describe('OrderDetailsPanel', () => {
provider: createUser('provider'),
customer: createUser('customer'),
});
const panel = shallow(<OrderDetailsPanel transaction={tx} />);
const panel = shallow(<OrderDetailsPanelComponent transaction={tx} intl={fakeIntl} />);
const breakdownProps = panel.find(BookingBreakdown).props();
expect(breakdownProps.transaction.attributes.payinTotal).toEqual(new Money(16500, 'USD'));
});

View file

@ -52,7 +52,9 @@ describe('ListingPage', () => {
return showListing(id)(dispatch, null, sdk).then(data => {
expect(data).toEqual(response);
expect(show.mock.calls).toEqual([[{ id, include: ['author', 'author.profileImage', 'images'] }]]);
expect(show.mock.calls).toEqual([
[{ id, include: ['author', 'author.profileImage', 'images'] }],
]);
expect(dispatch.mock.calls).toEqual([
[showListingRequest(id)],
[expect.anything()], // fetchCurrentUser() call
@ -76,7 +78,9 @@ describe('ListingPage', () => {
},
e => {
expect(e).toEqual(error);
expect(show.mock.calls).toEqual([[{ id, include: ['author', 'author.profileImage', 'images'] }]]);
expect(show.mock.calls).toEqual([
[{ id, include: ['author', 'author.profileImage', 'images'] }],
]);
expect(dispatch.mock.calls).toEqual([
[showListingRequest(id)],
[expect.anything()], // fetchCurrentUser() call

View file

@ -1,5 +1,6 @@
import { types } from '../../util/sdkLoader';
import { addMarketplaceEntities } from '../../ducks/marketplaceData.duck';
import { updatedEntities, denormalisedEntities } from '../../util/data';
// ================ Action types ================ //
@ -56,11 +57,20 @@ export const fetchOrder = id =>
.then(response => {
txResponse = response;
const listingId = listingRelationship(response).id;
const entities = updatedEntities({}, response.data);
const denormalised = denormalisedEntities(entities, 'listing', [listingId]);
const listing = denormalised[0];
return sdk.listings.show({
id: listingId,
include: ['author', 'author.profileImage', 'images'],
});
const canFetchListing = listing && listing.attributes && !listing.attributes.deleted;
if (canFetchListing) {
return sdk.listings.show({
id: listingId,
include: ['author', 'author.profileImage', 'images'],
});
} else {
return response;
}
})
.then(response => {
dispatch(addMarketplaceEntities(txResponse));

View file

@ -43,10 +43,8 @@ exports[`OrderPage matches snapshot 1`] = `
onResendVerificationEmail={[Function]}
sendVerificationEmailError={null}
sendVerificationEmailInProgress={false} />
<OrderDetailsPanel
<InjectIntl(Component)
className="undefined"
lastTransition={null}
rootClassName={null}
transaction={
Object {
"attributes": Object {

View file

@ -7,7 +7,11 @@ import * as propTypes from '../../util/propTypes';
import { sendVerificationEmail } from '../../ducks/user.duck';
import { logout, authenticationInProgress } from '../../ducks/Auth.duck';
import { manageDisableScrolling, isScrollingDisabled } from '../../ducks/UI.duck';
import { recoverPassword, retypePasswordRecoveryEmail, clearPasswordRecoveryError } from './PasswordRecoveryPage.duck';
import {
recoverPassword,
retypePasswordRecoveryEmail,
clearPasswordRecoveryError,
} from './PasswordRecoveryPage.duck';
import { PageLayout, Topbar, InlineTextButton, KeysIcon } from '../../components';
import { PasswordRecoveryForm } from '../../containers';

View file

@ -193,8 +193,12 @@
"MapPriceMarker.unsupportedPrice": "({currency})",
"Modal.close": "CLOSE",
"Modal.closeModal": "Close modal",
"OrderDetailsPanel.bannedUserDisplayName": "Banned user",
"OrderDetailsPanel.bookingBreakdownTitle": "Booking breakdown",
"OrderDetailsPanel.deletedListingOrderTitle": "a listing",
"OrderDetailsPanel.deletedListingTitle": "Deleted listing",
"OrderDetailsPanel.hostedBy": "Hosted by {name}",
"OrderDetailsPanel.messageDeletedListing": "However, the listing is deleted and cannot be viewed anymore.",
"OrderDetailsPanel.orderAcceptedStatus": "{providerName} accepted the request on {transitionDate}.",
"OrderDetailsPanel.orderAcceptedSubtitle": "You have booked {listingLink}",
"OrderDetailsPanel.orderAcceptedTitle": "Woohoo {customerName}!",