From 2b7e79b3b133cf64b393a195e96835ccff366345 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Thu, 9 Nov 2017 18:21:55 +0200 Subject: [PATCH 1/6] Add transitions to the transaction shape --- src/util/propTypes.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/util/propTypes.js b/src/util/propTypes.js index b983b275..e9be5dd4 100644 --- a/src/util/propTypes.js +++ b/src/util/propTypes.js @@ -172,6 +172,19 @@ export const TX_TRANSITIONS = [ TX_TRANSITION_MARK_DELIVERED, ]; +// 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, +]; + const txLastTransition = tx => ensureTransaction(tx).attributes.lastTransition; export const txIsPreauthorized = tx => txLastTransition(tx) === TX_TRANSITION_PREAUTHORIZE; @@ -207,6 +220,13 @@ export const transaction = shape({ reversal: bool.isRequired, }) ).isRequired, + transitions: arrayOf( + shape({ + at: instanceOf(Date).isRequired, + by: oneOf(TX_TRANSITION_ACTORS).isRequired, + transition: oneOf(TX_TRANSITIONS).isRequired, + }) + ).isRequired, }), booking, listing, From 1292484476d9531309aff37fd805d153354243e8 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Thu, 9 Nov 2017 18:45:28 +0200 Subject: [PATCH 2/6] Add tx transitions to test data --- .../BookingBreakdown.example.js | 7 + .../BookingBreakdown/BookingBreakdown.test.js | 7 + .../OrderDetailsPanel.test.js.snap | 165 ++++++++++++++-- .../SaleDetailsPanel.test.js.snap | 185 ++++++++++++++---- .../__snapshots__/InboxPage.test.js.snap | 48 +++++ .../__snapshots__/OrderPage.test.js.snap | 12 ++ .../__snapshots__/SalePage.test.js.snap | 12 ++ src/util/test-data.js | 15 +- 8 files changed, 397 insertions(+), 54 deletions(-) diff --git a/src/components/BookingBreakdown/BookingBreakdown.example.js b/src/components/BookingBreakdown/BookingBreakdown.example.js index b8dcda75..92769f50 100644 --- a/src/components/BookingBreakdown/BookingBreakdown.example.js +++ b/src/components/BookingBreakdown/BookingBreakdown.example.js @@ -22,6 +22,13 @@ const exampleTransaction = params => { createdAt: created, lastTransitionedAt: created, lastTransition: propTypes.TX_TRANSITION_PREAUTHORIZE, + transitions: [ + { + at: created, + by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER, + transition: propTypes.TX_TRANSITION_PREAUTHORIZE, + }, + ], // payinTotal, payoutTotal, and lineItems required in params ...params, diff --git a/src/components/BookingBreakdown/BookingBreakdown.test.js b/src/components/BookingBreakdown/BookingBreakdown.test.js index 29c90818..b4addd56 100644 --- a/src/components/BookingBreakdown/BookingBreakdown.test.js +++ b/src/components/BookingBreakdown/BookingBreakdown.test.js @@ -17,6 +17,13 @@ const exampleTransaction = params => { createdAt: created, lastTransitionedAt: created, lastTransition: propTypes.TX_TRANSITION_PREAUTHORIZE, + transitions: [ + { + at: created, + by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER, + transition: propTypes.TX_TRANSITION_PREAUTHORIZE, + }, + ], // payinTotal, payoutTotal, and lineItems required in params ...params, diff --git a/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap b/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap index 55b6410e..75df49f9 100644 --- a/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap +++ b/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap @@ -144,6 +144,18 @@ exports[`OrderDetailsPanel accepted matches snapshot 1`] = ` "amount": 16400, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -428,6 +440,18 @@ exports[`OrderDetailsPanel accepted matches snapshot 1`] = ` "amount": 16400, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -632,6 +656,18 @@ exports[`OrderDetailsPanel autodeclined matches snapshot 1`] = ` "amount": 16400, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -916,6 +952,18 @@ exports[`OrderDetailsPanel autodeclined matches snapshot 1`] = ` "amount": 16400, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -1043,7 +1091,7 @@ exports[`OrderDetailsPanel canceled matches snapshot 1`] = ` -

- -

-

-

- -

@@ -1002,7 +1040,7 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = ` Object { "attributes": Object { "createdAt": 2017-05-01T00:00:00.000Z, - "lastTransition": "transition/preauthorize", + "lastTransition": "transition/accept", "lastTransitionedAt": 2017-06-10T00:00:00.000Z, "lineItems": Array [ Object { @@ -1039,6 +1077,18 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = ` "amount": 15500, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -1095,7 +1145,7 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = ` />

-
-
- - - - - - -
@@ -1253,7 +1280,7 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = ` Object { "attributes": Object { "createdAt": 2017-05-01T00:00:00.000Z, - "lastTransition": "transition/preauthorize", + "lastTransition": "transition/accept", "lastTransitionedAt": 2017-06-10T00:00:00.000Z, "lineItems": Array [ Object { @@ -1290,6 +1317,18 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = ` "amount": 15500, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -1501,6 +1540,18 @@ exports[`SaleDetailsPanel declined matches snapshot 1`] = ` "amount": 15500, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -1729,6 +1780,18 @@ exports[`SaleDetailsPanel declined matches snapshot 1`] = ` "amount": 15500, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -1940,6 +2003,18 @@ exports[`SaleDetailsPanel delivered matches snapshot 1`] = ` "amount": 15500, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -2168,6 +2243,18 @@ exports[`SaleDetailsPanel delivered matches snapshot 1`] = ` "amount": 15500, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -2389,6 +2476,18 @@ exports[`SaleDetailsPanel preauthorized matches snapshot 1`] = ` "amount": 15500, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -2640,6 +2739,18 @@ exports[`SaleDetailsPanel preauthorized matches snapshot 1`] = ` "amount": 15500, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { diff --git a/src/containers/InboxPage/__snapshots__/InboxPage.test.js.snap b/src/containers/InboxPage/__snapshots__/InboxPage.test.js.snap index 8f117da9..18ebeb1c 100644 --- a/src/containers/InboxPage/__snapshots__/InboxPage.test.js.snap +++ b/src/containers/InboxPage/__snapshots__/InboxPage.test.js.snap @@ -130,6 +130,18 @@ exports[`InboxPage matches snapshot 1`] = ` "amount": 900, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -232,6 +244,18 @@ exports[`InboxPage matches snapshot 1`] = ` "amount": 900, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -492,6 +516,18 @@ exports[`InboxPage matches snapshot 3`] = ` "amount": 900, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { @@ -594,6 +630,18 @@ exports[`InboxPage matches snapshot 3`] = ` "amount": 900, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { diff --git a/src/containers/OrderPage/__snapshots__/OrderPage.test.js.snap b/src/containers/OrderPage/__snapshots__/OrderPage.test.js.snap index 2bcba8a0..fb1a0a35 100644 --- a/src/containers/OrderPage/__snapshots__/OrderPage.test.js.snap +++ b/src/containers/OrderPage/__snapshots__/OrderPage.test.js.snap @@ -89,6 +89,18 @@ exports[`OrderPage matches snapshot 1`] = ` "amount": 900, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { diff --git a/src/containers/SalePage/__snapshots__/SalePage.test.js.snap b/src/containers/SalePage/__snapshots__/SalePage.test.js.snap index 36a35668..9cc64457 100644 --- a/src/containers/SalePage/__snapshots__/SalePage.test.js.snap +++ b/src/containers/SalePage/__snapshots__/SalePage.test.js.snap @@ -95,6 +95,18 @@ exports[`SalePage matches snapshot 1`] = ` "amount": 900, "currency": "USD", }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], }, "booking": Object { "attributes": Object { diff --git a/src/util/test-data.js b/src/util/test-data.js index 5c329e3f..22de8248 100644 --- a/src/util/test-data.js +++ b/src/util/test-data.js @@ -89,7 +89,7 @@ export const createListing = (id, attributes = {}, includes = {}) => ({ export const createTransaction = options => { const { id, - lastTransition = propTypes.TX_TRANSITION_PREAUTHORIZE, + lastTransition = propTypes.TX_TRANSITION_ACCEPT, total = new Money(1000, 'USD'), commission = new Money(100, 'USD'), booking = null, @@ -97,6 +97,18 @@ export const createTransaction = options => { customer = null, provider = null, lastTransitionedAt = new Date(Date.UTC(2017, 5, 1)), + transitions = [ + { + at: new Date(Date.UTC(2017, 4, 1)), + by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER, + transition: propTypes.TX_TRANSITION_PREAUTHORIZE, + }, + { + at: new Date(Date.UTC(2017, 5, 1)), + by: propTypes.TX_TRANSITION_ACTOR_PROVIDER, + transition: propTypes.TX_TRANSITION_ACCEPT, + }, + ], } = options; const nightCount = booking ? nightsBetween(booking.attributes.start, booking.attributes.end) : 1; return { @@ -123,6 +135,7 @@ export const createTransaction = options => { reversal: false, }, ], + transitions, }, booking, listing, From 981990f7737ec233d1f1c2ee8eeae07a3183ae37 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Thu, 9 Nov 2017 19:03:16 +0200 Subject: [PATCH 3/6] Add transaction transitions to Messages component --- src/components/Messages/Messages.css | 18 +++ src/components/Messages/Messages.example.js | 50 ++++++- src/components/Messages/Messages.js | 157 +++++++++++++++++++- src/translations/en.json | 10 ++ src/util/propTypes.js | 14 +- src/util/test-data.js | 31 ++-- 6 files changed, 254 insertions(+), 26 deletions(-) diff --git a/src/components/Messages/Messages.css b/src/components/Messages/Messages.css index 51fc8ca2..272eb5a6 100644 --- a/src/components/Messages/Messages.css +++ b/src/components/Messages/Messages.css @@ -81,3 +81,21 @@ .ownMessageDate { text-align: right; } + +.transition { + display: flex; + flex-direction: row; +} + +.transitionContent { + margin: 0; +} +.transitionDate { + @apply --marketplaceMessageDateFontStyles; + margin: 0; + color: var(--matterColorAnti); +} + +.bullet { + margin-right: 6px; +} diff --git a/src/components/Messages/Messages.example.js b/src/components/Messages/Messages.example.js index f110c876..3db1e0c9 100644 --- a/src/components/Messages/Messages.example.js +++ b/src/components/Messages/Messages.example.js @@ -1,4 +1,12 @@ -import { createUser, createCurrentUser, createMessage } from '../../util/test-data'; +import { + createUser, + createCurrentUser, + createMessage, + createTransaction, + createListing, + createTxTransition, +} from '../../util/test-data'; +import * as propTypes from '../../util/propTypes'; import Messages from './Messages'; export const Empty = { @@ -6,6 +14,9 @@ export const Empty = { props: { messages: [], currentUser: null, + transaction: null, + customer: null, + provider: null, }, group: 'messages', }; @@ -34,3 +45,40 @@ export const WithCurrentUser = { }, group: 'messages', }; + +export const WithTransitions = { + component: Messages, + props: { + messages: [ + createMessage( + 'msg1', + { at: new Date(Date.UTC(2017, 10, 9, 8, 12)) }, + { sender: createUser('user1') } + ), + createMessage( + 'msg2', + { at: new Date(Date.UTC(2017, 10, 12, 13, 20)) }, + { sender: createUser('user2') } + ), + ], + transaction: createTransaction({ + customer: createUser('user1'), + provider: createUser('user2'), + listing: createListing('Listing'), + transitions: [ + createTxTransition({ + at: new Date(Date.UTC(2017, 10, 9, 8, 10)), + by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER, + transition: propTypes.TX_TRANSITION_PREAUTHORIZE, + }), + createTxTransition({ + at: new Date(Date.UTC(2017, 10, 9, 10, 33)), + by: propTypes.TX_TRANSITION_ACTOR_PROVIDER, + transition: propTypes.TX_TRANSITION_ACCEPT, + }), + ], + }), + currentUser: createCurrentUser('user2'), + }, + group: 'messages', +}; diff --git a/src/components/Messages/Messages.js b/src/components/Messages/Messages.js index bb490380..cd0b6943 100644 --- a/src/components/Messages/Messages.js +++ b/src/components/Messages/Messages.js @@ -4,6 +4,7 @@ import { injectIntl, intlShape } from 'react-intl'; import classNames from 'classnames'; import { Avatar } from '../../components'; import { formatDate } from '../../util/dates'; +import { ensureTransaction, ensureUser } from '../../util/data'; import * as propTypes from '../../util/propTypes'; import css from './Messages.css'; @@ -43,11 +44,133 @@ OwnMessage.propTypes = { intl: intlShape.isRequired, }; +const Transition = props => { + const { transition, currentUser, customer, provider, listingTitle, 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: 'Messages.ownTransitionRequest' }, { listingTitle }) + : intl.formatMessage({ id: 'Messages.transitionRequest' }, { displayName, listingTitle }); + case propTypes.TX_TRANSITION_ACCEPT: + return isOwnTransition + ? intl.formatMessage({ id: 'Messages.ownTransitionAccept' }) + : intl.formatMessage({ id: 'Messages.transitionAccept' }, { displayName }); + case propTypes.TX_TRANSITION_DECLINE: + return isOwnTransition + ? intl.formatMessage({ id: 'Messages.ownTransitionDecline' }) + : intl.formatMessage({ id: 'Messages.transitionDecline' }, { displayName }); + case propTypes.TX_TRANSITION_AUTO_DECLINE: + return ownRole === propTypes.TX_TRANSITION_ACTOR_PROVIDER + ? intl.formatMessage({ id: 'Messages.ownTransitionAutoDecline' }) + : intl.formatMessage({ id: 'Messages.transitionAutoDecline' }, { displayName }); + case propTypes.TX_TRANSITION_MARK_DELIVERED: + return intl.formatMessage({ id: 'Messages.transitionComplete' }); + case propTypes.TX_TRANSITION_CANCEL: + return intl.formatMessage({ id: 'Messages.transitionCancel' }); + + default: + return ''; + } + }; + + const ownRole = + currentUser.id.uuid === customer.id.uuid + ? propTypes.TX_TRANSITION_ACTOR_CUSTOMER + : propTypes.TX_TRANSITION_ACTOR_PROVIDER; + + const otherUsersName = + ownRole === propTypes.TX_TRANSITION_ACTOR_CUSTOMER + ? provider.attributes.profile.displayName + : customer.attributes.profile.displayName; + + const transitionMessage = resolveTransitionMessage(transition, ownRole, otherUsersName, intl); + const todayString = intl.formatMessage({ id: 'Messages.today' }); + + return ( +
+
+

+
+
+

{transitionMessage}

+

{formatDate(intl, todayString, transition.at)}

+
+
+ ); +}; + +Transition.propTypes = { + transition: propTypes.txTransition.isRequired, + currentUser: propTypes.currentUser.isRequired, + customer: propTypes.user.isRequired, + provider: propTypes.user.isRequired, + listingTitle: string.isRequired, + intl: intlShape.isRequired, +}; + +const EmptyTransition = () => { + return ( +
+
+

+
+
+

+

+

+
+ ); +}; + export const MessagesComponent = props => { - const { rootClassName, className, messages, currentUser, intl } = props; + const { rootClassName, className, messages, transaction, currentUser, intl } = props; const classes = classNames(rootClassName || css.root, className); - const msg = message => { + const currentTransaction = ensureTransaction(transaction); + const transitions = currentTransaction.attributes.transitions + ? currentTransaction.attributes.transitions + : []; + const currentCustomer = ensureUser(currentTransaction.customer); + const currentProvider = ensureUser(currentTransaction.provider); + + const transitionsAvailable = + currentUser && currentUser.id && currentCustomer.id && currentProvider.id; + + const isMessage = item => item && item.type === 'message'; + + // Compare function for sorting an array containint messages and transitions + const compareItems = (a, b) => { + const itemDate = item => (isMessage(item) ? item.attributes.at : item.at); + return itemDate(a) - itemDate(b); + }; + + // combine messages and transaction transitions + const items = messages.concat(transitions).sort(compareItems); + + const transitionComponent = transition => { + if (transitionsAvailable) { + return ( + + ); + } else { + return ; + } + }; + + const messageComponent = message => { const isOwnMessage = message.sender && message.sender.id && @@ -60,13 +183,32 @@ export const MessagesComponent = props => { return ; }; + const messageListItem = message => { + return ( +
  • + {messageComponent(message)} +
  • + ); + }; + + const transitionListItem = transition => { + const classes = classNames(css.messageItem, css.transition); + return ( +
  • + {transitionComponent(transition)} +
  • + ); + }; + return (
      - {messages.map(m => ( -
    • - {msg(m)} -
    • - ))} + {items.map(item => { + if (isMessage(item)) { + return messageListItem(item); + } else { + return transitionListItem(item); + } + })}
    ); }; @@ -81,6 +223,7 @@ MessagesComponent.propTypes = { className: string, messages: arrayOf(propTypes.message), + transaction: propTypes.transaction, currentUser: propTypes.currentUser, // from injectIntl diff --git a/src/translations/en.json b/src/translations/en.json index 45447ae3..4bff1626 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -248,7 +248,17 @@ "ManageListingsPage.youHaveListings": "You have {count} {count, plural, one {listing} other {listings}}", "ManageListingsPage.yourListings": "Your listings", "MapPriceMarker.unsupportedPrice": "({currency})", + "Messages.ownTransitionAccept": "You accepted the booking request.", + "Messages.ownTransitionAutoDecline": "Booking request expired. You did not reply in time.", + "Messages.ownTransitionDecline": "You declined the booking request.", + "Messages.ownTransitionRequest": "You requested to book { listingTitle }.", "Messages.today": "Today", + "Messages.transitionAccept": "{ displayName } accepted the booking request.", + "Messages.transitionAutoDecline": "Booking request expired. { displayName } did not reply in time.", + "Messages.transitionComplete": "Booking was completed.", + "Messages.transitionCancel": "Booking was cancelled.", + "Messages.transitionDecline": "{ displayName } declined the booking request.", + "Messages.transitionRequest": "{ displayName } requested to book { listingTitle }.", "Modal.close": "CLOSE", "Modal.closeModal": "Close modal", "NotFoundPage.description": "We can't find the page or the sauna you're looking for. Make sure you've typed the URL correctly, of try searching Saunatime.", diff --git a/src/util/propTypes.js b/src/util/propTypes.js index e9be5dd4..104994dd 100644 --- a/src/util/propTypes.js +++ b/src/util/propTypes.js @@ -201,6 +201,12 @@ export const txIsCanceled = tx => txLastTransition(tx) === TX_TRANSITION_CANCEL; export const txIsDelivered = tx => txLastTransition(tx) === TX_TRANSITION_MARK_DELIVERED; +export const txTransition = shape({ + at: instanceOf(Date).isRequired, + by: oneOf(TX_TRANSITION_ACTORS).isRequired, + transition: oneOf(TX_TRANSITIONS).isRequired, +}); + // Denormalised transaction object export const transaction = shape({ id: uuid.isRequired, @@ -220,13 +226,7 @@ export const transaction = shape({ reversal: bool.isRequired, }) ).isRequired, - transitions: arrayOf( - shape({ - at: instanceOf(Date).isRequired, - by: oneOf(TX_TRANSITION_ACTORS).isRequired, - transition: oneOf(TX_TRANSITIONS).isRequired, - }) - ).isRequired, + transitions: arrayOf(txTransition).isRequired, }), booking, listing, diff --git a/src/util/test-data.js b/src/util/test-data.js index 22de8248..d6767095 100644 --- a/src/util/test-data.js +++ b/src/util/test-data.js @@ -86,6 +86,15 @@ export const createListing = (id, attributes = {}, includes = {}) => ({ ...includes, }); +export const createTxTransition = options => { + return { + at: new Date(Date.UTC(2017, 4, 1)), + by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER, + transition: propTypes.TX_TRANSITION_PREAUTHORIZE, + ...options, + }; +}; + export const createTransaction = options => { const { id, @@ -98,17 +107,17 @@ export const createTransaction = options => { provider = null, lastTransitionedAt = new Date(Date.UTC(2017, 5, 1)), transitions = [ - { - at: new Date(Date.UTC(2017, 4, 1)), - by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER, - transition: propTypes.TX_TRANSITION_PREAUTHORIZE, - }, - { - at: new Date(Date.UTC(2017, 5, 1)), - by: propTypes.TX_TRANSITION_ACTOR_PROVIDER, - transition: propTypes.TX_TRANSITION_ACCEPT, - }, - ], + createTxTransition({ + at: new Date(Date.UTC(2017, 4, 1)), + by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER, + transition: propTypes.TX_TRANSITION_PREAUTHORIZE, + }), + createTxTransition({ + at: new Date(Date.UTC(2017, 5, 1)), + by: propTypes.TX_TRANSITION_ACTOR_PROVIDER, + transition: propTypes.TX_TRANSITION_ACCEPT, + }), + ], } = options; const nightCount = booking ? nightsBetween(booking.attributes.start, booking.attributes.end) : 1; return { From 27484e42cae2a855a1a352b30d5ac3e5669789e2 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Wed, 15 Nov 2017 16:06:54 +0200 Subject: [PATCH 4/6] Add transitions to OrderDetailsPanel --- .../OrderDetailsPanel/OrderDetailsPanel.js | 13 +- .../OrderDetailsPanel.test.js.snap | 702 ++++++++++++++++++ 2 files changed, 713 insertions(+), 2 deletions(-) diff --git a/src/components/OrderDetailsPanel/OrderDetailsPanel.js b/src/components/OrderDetailsPanel/OrderDetailsPanel.js index 7048ce91..4659dd76 100644 --- a/src/components/OrderDetailsPanel/OrderDetailsPanel.js +++ b/src/components/OrderDetailsPanel/OrderDetailsPanel.js @@ -155,7 +155,11 @@ export class OrderDetailsPanelComponent extends Component { const messagesContainerClasses = classNames(css.messagesContainer, { [css.messagesContainerWithInfoAbove]: showInfoMessage, }); - const showMessages = messages.length > 0 || initialMessageFailed || fetchMessagesError; + const txTransitions = currentTransaction.attributes.transitions + ? currentTransaction.attributes.transitions + : []; + const showMessages = + messages.length > 0 || txTransitions.length > 0 || initialMessageFailed || fetchMessagesError; const messagesContainer = showMessages ? (

    @@ -171,7 +175,12 @@ export class OrderDetailsPanelComponent extends Component {

    ) : null} - +

    ) : null; diff --git a/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap b/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap index 75df49f9..9d4c1f7c 100644 --- a/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap +++ b/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap @@ -307,6 +307,123 @@ exports[`OrderDetailsPanel accepted matches snapshot 1`] = ` }, ] } + transaction={ + Object { + "attributes": Object { + "createdAt": 2017-05-01T00:00:00.000Z, + "lastTransition": "transition/accept", + "lastTransitionedAt": 2017-06-01T00:00:00.000Z, + "lineItems": Array [ + Object { + "code": "line-item/night", + "lineTotal": Money { + "amount": 16500, + "currency": "USD", + }, + "quantity": "3", + "reversal": false, + "unitPrice": Money { + "amount": 5500, + "currency": "USD", + }, + }, + Object { + "code": "line-item/provider-commission", + "lineTotal": Money { + "amount": -100, + "currency": "USD", + }, + "reversal": false, + "unitPrice": Money { + "amount": -100, + "currency": "USD", + }, + }, + ], + "payinTotal": Money { + "amount": 16500, + "currency": "USD", + }, + "payoutTotal": Money { + "amount": 16400, + "currency": "USD", + }, + "transitions": Array [ + Object { + "at": 2017-05-01T00:00:00.000Z, + "by": "customer", + "transition": "transition/preauthorize", + }, + Object { + "at": 2017-06-01T00:00:00.000Z, + "by": "provider", + "transition": "transition/accept", + }, + ], + }, + "booking": Object { + "attributes": Object { + "end": 2017-06-13T00:00:00.000Z, + "start": 2017-06-10T00:00:00.000Z, + }, + "id": UUID { + "uuid": "booking1", + }, + "type": "booking", + }, + "customer": Object { + "attributes": Object { + "banned": false, + "profile": Object { + "abbreviatedName": "TT", + "displayName": "customer display name", + }, + }, + "id": UUID { + "uuid": "customer", + }, + "type": "user", + }, + "id": UUID { + "uuid": "order-accepted", + }, + "listing": Object { + "attributes": Object { + "address": "listing1 address", + "closed": false, + "deleted": false, + "description": "listing1 description", + "geolocation": LatLng { + "lat": 40, + "lng": 60, + }, + "price": Money { + "amount": 5500, + "currency": "USD", + }, + "title": "listing1 title", + }, + "id": UUID { + "uuid": "listing1", + }, + "type": "listing", + }, + "provider": Object { + "attributes": Object { + "banned": false, + "profile": Object { + "abbreviatedName": "TT", + "displayName": "provider display name", + }, + }, + "id": UUID { + "uuid": "provider", + }, + "type": "user", + }, + "type": "transaction", + } + } />

    Date: Thu, 16 Nov 2017 14:06:35 +0200 Subject: [PATCH 5/6] Fix baseline alignment --- src/components/Messages/Messages.css | 32 +++++++++++++++++++++ src/components/Messages/Messages.example.js | 24 ++++++++++++++-- src/components/Messages/Messages.js | 3 +- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/components/Messages/Messages.css b/src/components/Messages/Messages.css index 272eb5a6..a7364192 100644 --- a/src/components/Messages/Messages.css +++ b/src/components/Messages/Messages.css @@ -82,13 +82,45 @@ text-align: right; } +.transitionItem { + margin-bottom: 34px; + + /* Clearfix */ + @apply --clearfix; + + @media (--viewportMedium) { + margin-bottom: 40px; + } + + &:last-of-type { + margin-bottom: 0; + } +} + .transition { display: flex; flex-direction: row; + margin: -1px 0 1px 0; + + @media (--viewportMedium) { + margin: 0; + } } .transitionContent { margin: 0; + + /* Font */ + font-family: 'sofiapro', Helvetica, Arial, sans-serif; + font-weight: var(--fontWeightRegular); + font-size: 16px; + line-height: 20px; + + @media (--viewportMedium) { + font-size: 20px; + line-height: 32px; + margin: 1px 0 -1px 0; + } } .transitionDate { @apply --marketplaceMessageDateFontStyles; diff --git a/src/components/Messages/Messages.example.js b/src/components/Messages/Messages.example.js index 3db1e0c9..f23b47f5 100644 --- a/src/components/Messages/Messages.example.js +++ b/src/components/Messages/Messages.example.js @@ -52,11 +52,21 @@ export const WithTransitions = { messages: [ createMessage( 'msg1', - { at: new Date(Date.UTC(2017, 10, 9, 8, 12)) }, + { at: new Date(Date.UTC(2017, 10, 9, 8, 11)) }, { sender: createUser('user1') } ), createMessage( 'msg2', + { at: new Date(Date.UTC(2017, 10, 9, 8, 14)) }, + { sender: createUser('user1') } + ), + createMessage( + 'msg3', + { at: new Date(Date.UTC(2017, 10, 9, 8, 17)) }, + { sender: createUser('user2') } + ), + createMessage( + 'msg4', { at: new Date(Date.UTC(2017, 10, 12, 13, 20)) }, { sender: createUser('user2') } ), @@ -72,10 +82,20 @@ export const WithTransitions = { transition: propTypes.TX_TRANSITION_PREAUTHORIZE, }), createTxTransition({ - at: new Date(Date.UTC(2017, 10, 9, 10, 33)), + at: new Date(Date.UTC(2017, 10, 9, 8, 12)), by: propTypes.TX_TRANSITION_ACTOR_PROVIDER, transition: propTypes.TX_TRANSITION_ACCEPT, }), + createTxTransition({ + at: new Date(Date.UTC(2017, 10, 9, 10, 33)), + by: propTypes.TX_TRANSITION_ACTOR_PROVIDER, + transition: propTypes.TX_TRANSITION_DECLINE, + }), + createTxTransition({ + at: new Date(Date.UTC(2017, 10, 9, 10, 34)), + by: propTypes.TX_TRANSITION_ACTOR_PROVIDER, + transition: propTypes.TX_TRANSITION_MARK_DELIVERED, + }), ], }), currentUser: createCurrentUser('user2'), diff --git a/src/components/Messages/Messages.js b/src/components/Messages/Messages.js index cd0b6943..a5f19d2b 100644 --- a/src/components/Messages/Messages.js +++ b/src/components/Messages/Messages.js @@ -192,9 +192,8 @@ export const MessagesComponent = props => { }; const transitionListItem = transition => { - const classes = classNames(css.messageItem, css.transition); return ( -
  • +
  • {transitionComponent(transition)}
  • ); From 5febb174dd123d89d412c1f162af84f2948c9a4e Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Fri, 17 Nov 2017 13:45:34 +0200 Subject: [PATCH 6/6] Fix transition line height and component baseline Changes baseline handling so that transitions and messages can occur in any order without affecting the baseline. Also Moves transition font styles to marketplaceFonts.css file. --- src/components/Messages/Messages.css | 39 ++++++++++----------- src/components/Messages/Messages.example.js | 26 ++++++++++++++ src/marketplaceFonts.css | 12 +++++++ 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/components/Messages/Messages.css b/src/components/Messages/Messages.css index a7364192..c7958c17 100644 --- a/src/components/Messages/Messages.css +++ b/src/components/Messages/Messages.css @@ -8,13 +8,13 @@ } .messageItem { - margin-bottom: 35px; + margin-bottom: 38px; /* Clearfix */ @apply --clearfix; @media (--viewportMedium) { - margin-bottom: 40px; + margin-bottom: 41px; } &:last-of-type { @@ -41,7 +41,11 @@ .avatar { flex-shrink: 0; - margin: 9px 12px 0 0; + margin: 6px 12px 0 0; + + @media (--viewportMedium) { + margin: 9px 12px 0 0; + } } .messageContent, @@ -49,11 +53,12 @@ @apply --marketplaceMessageFontStyles; margin: 0; - padding: 15px 14px 11px 14px; + padding: 12px 14px 11px 14px; border-radius: 8px; box-shadow: var(--boxShadow); @media (--viewportMedium) { + padding: 14px 14px 11px 14px; margin: 0; } } @@ -83,7 +88,7 @@ } .transitionItem { - margin-bottom: 34px; + margin-bottom: 36px; /* Clearfix */ @apply --clearfix; @@ -100,32 +105,26 @@ .transition { display: flex; flex-direction: row; - margin: -1px 0 1px 0; + padding: 3px 0 2px 0; @media (--viewportMedium) { - margin: 0; + padding: 0; } } .transitionContent { + @apply --marketplaceTxTransitionFontStyles; margin: 0; - - /* Font */ - font-family: 'sofiapro', Helvetica, Arial, sans-serif; - font-weight: var(--fontWeightRegular); - font-size: 16px; - line-height: 20px; - - @media (--viewportMedium) { - font-size: 20px; - line-height: 32px; - margin: 1px 0 -1px 0; - } } + .transitionDate { @apply --marketplaceMessageDateFontStyles; - margin: 0; color: var(--matterColorAnti); + margin: 7px 0 0 0; + + @media (--viewportMedium) { + margin: -1px 0 1px 0; + } } .bullet { diff --git a/src/components/Messages/Messages.example.js b/src/components/Messages/Messages.example.js index f23b47f5..f16ad482 100644 --- a/src/components/Messages/Messages.example.js +++ b/src/components/Messages/Messages.example.js @@ -47,6 +47,32 @@ export const WithCurrentUser = { }; export const WithTransitions = { + component: Messages, + props: { + messages: [], + transaction: createTransaction({ + customer: createUser('user1'), + provider: createUser('user2'), + listing: createListing('Listing'), + transitions: [ + createTxTransition({ + at: new Date(Date.UTC(2017, 10, 9, 8, 10)), + by: propTypes.TX_TRANSITION_ACTOR_CUSTOMER, + transition: propTypes.TX_TRANSITION_PREAUTHORIZE, + }), + createTxTransition({ + at: new Date(Date.UTC(2017, 10, 9, 8, 12)), + by: propTypes.TX_TRANSITION_ACTOR_PROVIDER, + transition: propTypes.TX_TRANSITION_ACCEPT, + }), + ], + }), + currentUser: createCurrentUser('user2'), + }, + group: 'messages', +}; + +export const WithMessagesAndTransitions = { component: Messages, props: { messages: [ diff --git a/src/marketplaceFonts.css b/src/marketplaceFonts.css index 5ef40a57..64b3c4f3 100644 --- a/src/marketplaceFonts.css +++ b/src/marketplaceFonts.css @@ -231,6 +231,18 @@ } } + --marketplaceTxTransitionFontStyles { + font-family: 'sofiapro', Helvetica, Arial, sans-serif; + font-weight: var(--fontWeightMedium); + font-size: 16px; + line-height: 18px; + + @media (--viewportMedium) { + font-size: 20px; + line-height: 32px; + } + } + /* ================ Tabbed navigation font styles ================ */ --marketplaceTabNavFontStyles {