Add reviews to the ActivityFeed component

This commit is contained in:
Hannu Lyytikainen 2017-11-23 15:21:45 +02:00
parent efc8616466
commit dccf787f13
11 changed files with 346 additions and 47 deletions

View file

@ -137,6 +137,32 @@
margin-right: 6px;
}
.reviewContent {
@apply --marketplaceH4FontStyles;
font-style: italic;
white-space: pre-line;
margin: 8px 0 0 0;
@media (--viewportMedium) {
max-width: 500px;
margin: 7px 0 0 0;
}
}
.reviewStars {
margin-top: 4px;
@media (--viewportMedium) {
margin-top: 2px;
}
}
.reviewStar {
width: 12px;
height: 12px;
font-styles: initial;
}
.showOlderWrapper {
text-align: center;
margin-bottom: 1px;

View file

@ -6,6 +6,7 @@ import {
createTransaction,
createListing,
createTxTransition,
createReview,
} from '../../util/test-data';
import * as propTypes from '../../util/propTypes';
import ActivityFeed from './ActivityFeed';
@ -84,7 +85,7 @@ export const WithTransitions = {
group: 'messages',
};
export const WithMessagesAndTransitions = {
export const WithMessagesTransitionsAndReviews = {
component: ActivityFeed,
props: {
currentUser: createCurrentUser('user2'),
@ -92,6 +93,7 @@ export const WithMessagesAndTransitions = {
customer: createUser('user1'),
provider: createUser('user2'),
listing: createListing('Listing'),
lastTransition: propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_SECOND,
transitions: [
createTxTransition({
at: new Date(Date.UTC(2017, 10, 9, 8, 10)),
@ -113,6 +115,28 @@ export const WithMessagesAndTransitions = {
by: propTypes.TX_TRANSITION_ACTOR_PROVIDER,
transition: propTypes.TX_TRANSITION_MARK_DELIVERED,
}),
createTxTransition({
at: new Date(Date.UTC(2017, 10, 9, 11, 34)),
by: propTypes.TX_TRANSITION_ACTOR_PROVIDER,
transition: propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_FIRST,
}),
createTxTransition({
at: new Date(Date.UTC(2017, 10, 9, 12, 34)),
by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER,
transition: propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_SECOND,
}),
],
reviews: [
createReview(
'review1',
{ at: new Date(Date.UTC(2017, 10, 9, 11, 34)), rating: 3, type: 'ofCustomer' },
{ author: createUser('user2'), subject: createUser('user1') }
),
createReview(
'review2',
{ at: new Date(Date.UTC(2017, 10, 9, 12, 34)), rating: 5, type: 'ofProvider' },
{ author: createUser('user1'), subject: createUser('user2') }
),
],
}),
messages: [
@ -144,6 +168,48 @@ export const WithMessagesAndTransitions = {
group: 'messages',
};
export const WithAReviewFromBothUsers = {
component: ActivityFeed,
props: {
currentUser: createCurrentUser('user1'),
transaction: createTransaction({
customer: createUser('user1'),
provider: createUser('user2'),
listing: createListing('Listing'),
reviews: [
createReview(
'review1',
{ at: new Date(Date.UTC(2017, 10, 9, 8, 10)), rating: 3, type: 'ofProvider' },
{ author: createUser('user1'), subject: createUser('user2') }
),
createReview(
'review2',
{ at: new Date(Date.UTC(2017, 10, 10, 8, 10)), rating: 5, type: 'ofCustomer' },
{ author: createUser('user2'), subject: createUser('user1') }
),
],
lastTransition: propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_SECOND,
transitions: [
createTxTransition({
at: new Date(Date.UTC(2017, 10, 9, 8, 10)),
by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER,
transition: propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_FIRST,
}),
createTxTransition({
at: new Date(Date.UTC(2017, 10, 10, 8, 10)),
by: propTypes.TX_TRANSITION_ACTOR_PROVIDER,
transition: propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_SECOND,
}),
],
}),
messages: [],
hasOlderMessages: false,
onShowOlderMessages: noop,
fetchMessagesInProgress: false,
},
group: 'messages',
};
class PagedFeed extends Component {
constructor(props) {
super(props);

View file

@ -1,9 +1,9 @@
import React from 'react';
import { string, arrayOf, bool, func } from 'prop-types';
import { string, arrayOf, bool, func, number } from 'prop-types';
import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
import { dropWhile } from 'lodash';
import classNames from 'classnames';
import { Avatar, InlineTextButton } from '../../components';
import { Avatar, InlineTextButton, NamedLink, ReviewRating } from '../../components';
import { formatDate } from '../../util/dates';
import { ensureTransaction, ensureUser, ensureListing } from '../../util/data';
import * as propTypes from '../../util/propTypes';
@ -47,43 +47,156 @@ OwnMessage.propTypes = {
intl: intlShape.isRequired,
};
const Review = props => {
const { content, rating } = props;
return (
<div>
<p className={css.reviewContent}>{content}</p>
<ReviewRating
reviewStarClassName={css.reviewStar}
className={css.reviewStars}
rating={rating}
/>
</div>
);
};
Review.propTypes = {
content: string.isRequired,
rating: number.isRequired,
};
const areReviewsCompleted = transition => {
return [
propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_SECOND,
propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_SECOND,
propTypes.TX_TRANSITION_MARK_REVIEWED_BY_CUSTOMER,
propTypes.TX_TRANSITION_MARK_REVIEWED_BY_PROVIDER,
propTypes.TX_TRANSITION_AUTO_COMPLETE_WITHOUT_REVIEWS,
].includes(transition);
};
const isReviewTransition = transition => {
return [
propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_FIRST,
propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_FIRST,
propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_SECOND,
propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_SECOND,
].includes(transition);
};
const hasUserLeftAReviewFirst = (userRole, lastTransition) => {
return (
(lastTransition === propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_FIRST &&
userRole === propTypes.TX_TRANSITION_ACTOR_CUSTOMER) ||
(lastTransition === propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_FIRST &&
userRole === propTypes.TX_TRANSITION_ACTOR_PROVIDER) ||
areReviewsCompleted(lastTransition)
);
};
const resolveTransitionMessage = (
transition,
lastTransition,
listingTitle,
ownRole,
otherUsersName,
intl
) => {
const isOwnTransition = transition.by === ownRole;
const currentTransition = transition.transition;
const displayName = otherUsersName;
const deliveredState = lastTransition === propTypes.TX_TRANSITION_MARK_DELIVERED;
switch (currentTransition) {
case propTypes.TX_TRANSITION_PREAUTHORIZE:
return isOwnTransition ? (
<FormattedMessage id="ActivityFeed.ownTransitionRequest" values={{ listingTitle }} />
) : (
<FormattedMessage
id="ActivityFeed.transitionRequest"
values={{ displayName, listingTitle }}
/>
);
case propTypes.TX_TRANSITION_ACCEPT:
return isOwnTransition ? (
<FormattedMessage id="ActivityFeed.ownTransitionAccept" />
) : (
<FormattedMessage id="ActivityFeed.transitionAccept" values={{ displayName }} />
);
case propTypes.TX_TRANSITION_DECLINE:
return isOwnTransition ? (
<FormattedMessage id="ActivityFeed.ownTransitionDecline" />
) : (
<FormattedMessage id="ActivityFeed.transitionDecline" />
);
case propTypes.TX_TRANSITION_AUTO_DECLINE:
return ownRole === propTypes.TX_TRANSITION_ACTOR_PROVIDER ? (
<FormattedMessage id="ActivityFeed.ownTransitionAutoDecline" />
) : (
<FormattedMessage id="ActivityFeed.transitionAutoDecline" values={{ displayName }} />
);
case propTypes.TX_TRANSITION_CANCEL:
return <FormattedMessage id="ActivityFeed.transitionCancel" />;
case propTypes.TX_TRANSITION_MARK_DELIVERED:
// Show the leave a review link if the state is delivered or
// if current user is not the first to leave a review
const reviewLink =
deliveredState || !hasUserLeftAReviewFirst(ownRole, lastTransition) ? (
<NamedLink name="LandingPage">
<FormattedMessage id="ActivityFeed.leaveAReview" values={{ displayName }} />
</NamedLink>
) : null;
return <FormattedMessage id="ActivityFeed.transitionComplete" values={{ reviewLink }} />;
case propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_FIRST:
case propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_FIRST:
if (isOwnTransition) {
return <FormattedMessage id="ActivityFeed.ownTransitionReview" values={{ displayName }} />;
} else {
// show the leave a review link if current user is not the first
// one to leave a review
const reviewLink = !hasUserLeftAReviewFirst(ownRole, lastTransition) ? (
<NamedLink name="LandingPage">
<FormattedMessage id="ActivityFeed.leaveAReviewSecond" values={{ displayName }} />
</NamedLink>
) : null;
return (
<FormattedMessage
id="ActivityFeed.transitionReview"
values={{ displayName, reviewLink }}
/>
);
}
case propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_SECOND:
case propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_SECOND:
if (isOwnTransition) {
return <FormattedMessage id="ActivityFeed.ownTransitionReview" values={{ displayName }} />;
} else {
return (
<FormattedMessage
id="ActivityFeed.transitionReview"
values={{ displayName, reviewLink: null }}
/>
);
}
default:
return '';
}
};
const reviewByAuthorId = (transaction, userId) => {
return transaction.reviews.filter(r => r.author.id.uuid === userId.uuid)[0];
};
const Transition = props => {
const { transition, currentUser, customer, provider, listingTitle, intl } = props;
const { transition, transaction, currentUser, intl } = props;
const resolveTransitionMessage = (transition, ownRole, otherUsersName, intl) => {
const isOwnTransition = transition.by === ownRole;
const transitionType = transition.transition;
const displayName = otherUsersName;
switch (transitionType) {
case propTypes.TX_TRANSITION_PREAUTHORIZE:
return isOwnTransition
? intl.formatMessage({ id: 'ActivityFeed.ownTransitionRequest' }, { listingTitle })
: intl.formatMessage(
{ id: 'ActivityFeed.transitionRequest' },
{ displayName, listingTitle }
);
case propTypes.TX_TRANSITION_ACCEPT:
return isOwnTransition
? intl.formatMessage({ id: 'ActivityFeed.ownTransitionAccept' })
: intl.formatMessage({ id: 'ActivityFeed.transitionAccept' }, { displayName });
case propTypes.TX_TRANSITION_DECLINE:
return isOwnTransition
? intl.formatMessage({ id: 'ActivityFeed.ownTransitionDecline' })
: intl.formatMessage({ id: 'ActivityFeed.transitionDecline' }, { displayName });
case propTypes.TX_TRANSITION_AUTO_DECLINE:
return ownRole === propTypes.TX_TRANSITION_ACTOR_PROVIDER
? intl.formatMessage({ id: 'ActivityFeed.ownTransitionAutoDecline' })
: intl.formatMessage({ id: 'ActivityFeed.transitionAutoDecline' }, { displayName });
case propTypes.TX_TRANSITION_MARK_DELIVERED:
return intl.formatMessage({ id: 'ActivityFeed.transitionComplete' });
case propTypes.TX_TRANSITION_CANCEL:
return intl.formatMessage({ id: 'ActivityFeed.transitionCancel' });
default:
return '';
}
};
const customer = transaction.customer;
const provider = transaction.provider;
const listingTitle = transaction.listing.attributes.title;
const lastTransition = transaction.attributes.lastTransition;
const ownRole =
currentUser.id.uuid === customer.id.uuid
@ -95,7 +208,38 @@ const Transition = props => {
? provider.attributes.profile.displayName
: customer.attributes.profile.displayName;
const transitionMessage = resolveTransitionMessage(transition, ownRole, otherUsersName, intl);
const transitionMessage = resolveTransitionMessage(
transition,
lastTransition,
listingTitle,
ownRole,
otherUsersName,
intl
);
const currentTransition = transition.transition;
let reviewComponent = null;
if (isReviewTransition(currentTransition) && areReviewsCompleted(lastTransition)) {
const customerReview =
currentTransition === propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_FIRST ||
currentTransition === propTypes.TX_TRANSITION_REVIEW_BY_CUSTOMER_SECOND;
const providerReview =
currentTransition === propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_FIRST ||
currentTransition === propTypes.TX_TRANSITION_REVIEW_BY_PROVIDER_SECOND;
if (customerReview) {
const review = reviewByAuthorId(transaction, customer.id);
reviewComponent = (
<Review content={review.attributes.content} rating={review.attributes.rating} />
);
} else if (providerReview) {
const review = reviewByAuthorId(transaction, provider.id);
reviewComponent = (
<Review content={review.attributes.content} rating={review.attributes.rating} />
);
}
}
const todayString = intl.formatMessage({ id: 'ActivityFeed.today' });
return (
@ -106,6 +250,7 @@ const Transition = props => {
<div>
<p className={css.transitionContent}>{transitionMessage}</p>
<p className={css.transitionDate}>{formatDate(intl, todayString, transition.at)}</p>
{reviewComponent}
</div>
</div>
);
@ -113,10 +258,8 @@ const Transition = props => {
Transition.propTypes = {
transition: propTypes.txTransition.isRequired,
transaction: propTypes.transaction.isRequired,
currentUser: propTypes.currentUser.isRequired,
customer: propTypes.user.isRequired,
provider: propTypes.user.isRequired,
listingTitle: string.isRequired,
intl: intlShape.isRequired,
};
@ -180,7 +323,8 @@ export const ActivityFeedComponent = props => {
currentUser &&
currentUser.id &&
currentCustomer.id &&
currentProvider.id
currentProvider.id &&
currentListing.id
);
// combine messages and transaction transitions
@ -191,10 +335,8 @@ export const ActivityFeedComponent = props => {
return (
<Transition
transition={transition}
transaction={currentTransaction}
currentUser={currentUser}
customer={currentCustomer}
provider={currentProvider}
listingTitle={currentListing.attributes.title}
intl={intl}
/>
);

View file

@ -217,6 +217,7 @@ exports[`OrderDetailsPanel accepted matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -424,6 +425,7 @@ exports[`OrderDetailsPanel accepted matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -633,6 +635,7 @@ exports[`OrderDetailsPanel accepted matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -849,6 +852,7 @@ exports[`OrderDetailsPanel autodeclined matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -1056,6 +1060,7 @@ exports[`OrderDetailsPanel autodeclined matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -1265,6 +1270,7 @@ exports[`OrderDetailsPanel autodeclined matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -1493,6 +1499,7 @@ exports[`OrderDetailsPanel canceled matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -1700,6 +1707,7 @@ exports[`OrderDetailsPanel canceled matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -1909,6 +1917,7 @@ exports[`OrderDetailsPanel canceled matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -2125,6 +2134,7 @@ exports[`OrderDetailsPanel declined matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -2332,6 +2342,7 @@ exports[`OrderDetailsPanel declined matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -2541,6 +2552,7 @@ exports[`OrderDetailsPanel declined matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -2757,6 +2769,7 @@ exports[`OrderDetailsPanel delivered matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -2964,6 +2977,7 @@ exports[`OrderDetailsPanel delivered matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -3173,6 +3187,7 @@ exports[`OrderDetailsPanel delivered matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -3412,6 +3427,7 @@ exports[`OrderDetailsPanel preauthorized matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -3619,6 +3635,7 @@ exports[`OrderDetailsPanel preauthorized matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -3828,6 +3845,7 @@ exports[`OrderDetailsPanel preauthorized matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}

View file

@ -211,6 +211,7 @@ exports[`SaleDetailsPanel accepted matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -406,6 +407,7 @@ exports[`SaleDetailsPanel accepted matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -571,6 +573,7 @@ exports[`SaleDetailsPanel accepted matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -795,6 +798,7 @@ exports[`SaleDetailsPanel autodeclined matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -990,6 +994,7 @@ exports[`SaleDetailsPanel autodeclined matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -1155,6 +1160,7 @@ exports[`SaleDetailsPanel autodeclined matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -1379,6 +1385,7 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -1574,6 +1581,7 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -1739,6 +1747,7 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -1963,6 +1972,7 @@ exports[`SaleDetailsPanel declined matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -2158,6 +2168,7 @@ exports[`SaleDetailsPanel declined matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -2323,6 +2334,7 @@ exports[`SaleDetailsPanel declined matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -2547,6 +2559,7 @@ exports[`SaleDetailsPanel delivered matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -2742,6 +2755,7 @@ exports[`SaleDetailsPanel delivered matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -2907,6 +2921,7 @@ exports[`SaleDetailsPanel delivered matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -3141,6 +3156,7 @@ exports[`SaleDetailsPanel preauthorized matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -3336,6 +3352,7 @@ exports[`SaleDetailsPanel preauthorized matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}
@ -3529,6 +3546,7 @@ exports[`SaleDetailsPanel preauthorized matches snapshot 1`] = `
"type": "listing",
},
"provider": null,
"reviews": Array [],
"type": "transaction",
}
}

View file

@ -183,6 +183,7 @@ exports[`InboxPage matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -297,6 +298,7 @@ exports[`InboxPage matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -569,6 +571,7 @@ exports[`InboxPage matches snapshot 3`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}
@ -683,6 +686,7 @@ exports[`InboxPage matches snapshot 3`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}

View file

@ -165,6 +165,7 @@ exports[`OrderPage matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}

View file

@ -170,6 +170,7 @@ exports[`SalePage matches snapshot 1`] = `
},
"type": "user",
},
"reviews": Array [],
"type": "transaction",
}
}

View file

@ -1,16 +1,20 @@
{
"ActivityFeed.leaveAReview": "Leave a review for { displayName }.",
"ActivityFeed.leaveAReviewSecond": "Leave a review for { displayName } to see their review.",
"ActivityFeed.ownTransitionAccept": "You accepted the booking request.",
"ActivityFeed.ownTransitionAutoDecline": "Booking request expired. You did not reply on time.",
"ActivityFeed.ownTransitionDecline": "You declined the booking request.",
"ActivityFeed.ownTransitionRequest": "You requested to book { listingTitle }.",
"ActivityFeed.ownTransitionReview": "You left a review for { displayName }.",
"ActivityFeed.showOlderMessages": "Show older",
"ActivityFeed.today": "Today",
"ActivityFeed.transitionAccept": "{ displayName } accepted the booking request.",
"ActivityFeed.transitionAutoDecline": "Booking request expired. { displayName } did not reply on time.",
"ActivityFeed.transitionCancel": "The booking was cancelled.",
"ActivityFeed.transitionComplete": "The booking was completed.",
"ActivityFeed.transitionComplete": "The booking was completed. { reviewLink }",
"ActivityFeed.transitionDecline": "{ displayName } declined the booking request.",
"ActivityFeed.transitionRequest": "{ displayName } requested to book { listingTitle }.",
"ActivityFeed.transitionReview": "{ displayName } left a review for you. { reviewLink }",
"AuthenticationPage.emailAlreadyInUse": "An account with this email address already exists. Try logging in instead.",
"AuthenticationPage.fixEmail": "Whoops, typo in your email? {fixEmailLink}.",
"AuthenticationPage.fixEmailLinkText": "Fix it",

View file

@ -263,6 +263,7 @@ export const transaction = shape({
listing,
customer: user,
provider: user,
reviews: arrayOf(review),
});
// Denormalised transaction message

View file

@ -105,6 +105,7 @@ export const createTransaction = options => {
listing = null,
customer = null,
provider = null,
reviews = [],
lastTransitionedAt = new Date(Date.UTC(2017, 5, 1)),
transitions = [
createTxTransition({
@ -150,6 +151,7 @@ export const createTransaction = options => {
listing,
customer,
provider,
reviews,
};
};
@ -166,6 +168,22 @@ export const createMessage = (id, attributes = {}, includes = {}) => {
};
};
export const createReview = (id, attributes = {}, includes = {}) => {
return {
id: new UUID(id),
attributes: {
at: new Date(),
content:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
rating: 3,
state: 'public',
type: 'ofProvider',
...attributes,
},
...includes,
};
};
// Default config for currency formatting in tests and examples.
export const currencyConfig = {
style: 'currency',