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/Messages/Messages.css b/src/components/Messages/Messages.css index 51fc8ca2..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; } } @@ -81,3 +86,47 @@ .ownMessageDate { text-align: right; } + +.transitionItem { + margin-bottom: 36px; + + /* Clearfix */ + @apply --clearfix; + + @media (--viewportMedium) { + margin-bottom: 40px; + } + + &:last-of-type { + margin-bottom: 0; + } +} + +.transition { + display: flex; + flex-direction: row; + padding: 3px 0 2px 0; + + @media (--viewportMedium) { + padding: 0; + } +} + +.transitionContent { + @apply --marketplaceTxTransitionFontStyles; + margin: 0; +} + +.transitionDate { + @apply --marketplaceMessageDateFontStyles; + color: var(--matterColorAnti); + margin: 7px 0 0 0; + + @media (--viewportMedium) { + margin: -1px 0 1px 0; + } +} + +.bullet { + margin-right: 6px; +} diff --git a/src/components/Messages/Messages.example.js b/src/components/Messages/Messages.example.js index f110c876..f16ad482 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,86 @@ export const WithCurrentUser = { }, group: 'messages', }; + +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: [ + createMessage( + 'msg1', + { 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') } + ), + ], + 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, + }), + 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'), + }, + group: 'messages', +}; diff --git a/src/components/Messages/Messages.js b/src/components/Messages/Messages.js index bb490380..a5f19d2b 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,31 @@ export const MessagesComponent = props => { return ; }; + const messageListItem = message => { + return ( +
  • + {messageComponent(message)} +
  • + ); + }; + + const transitionListItem = 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 +222,7 @@ MessagesComponent.propTypes = { className: string, messages: arrayOf(propTypes.message), + transaction: propTypes.transaction, currentUser: propTypes.currentUser, // from injectIntl 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 55b6410e..9d4c1f7c 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 { @@ -295,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", + } + } /> -

    - -

    -

    -

    - -

    @@ -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/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 { 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 b983b275..104994dd 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; @@ -188,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, @@ -207,6 +226,7 @@ export const transaction = shape({ reversal: bool.isRequired, }) ).isRequired, + transitions: arrayOf(txTransition).isRequired, }), booking, listing, diff --git a/src/util/test-data.js b/src/util/test-data.js index 5c329e3f..d6767095 100644 --- a/src/util/test-data.js +++ b/src/util/test-data.js @@ -86,10 +86,19 @@ 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, - lastTransition = propTypes.TX_TRANSITION_PREAUTHORIZE, + lastTransition = propTypes.TX_TRANSITION_ACCEPT, total = new Money(1000, 'USD'), commission = new Money(100, 'USD'), booking = null, @@ -97,6 +106,18 @@ export const createTransaction = options => { customer = null, provider = null, lastTransitionedAt = new Date(Date.UTC(2017, 5, 1)), + transitions = [ + 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 { @@ -123,6 +144,7 @@ export const createTransaction = options => { reversal: false, }, ], + transitions, }, booking, listing,