Compare commits
27 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fa386fb0a | ||
|
|
1c77fadc77 | ||
|
|
3fb0c12770 | ||
|
|
1968d26de8 | ||
|
|
57c243f191 | ||
|
|
e2213a8b18 | ||
|
|
dc6b6f813f | ||
|
|
3ee4612504 | ||
|
|
483c590aef | ||
|
|
64351bb3a2 | ||
|
|
7ac10dc42e | ||
|
|
54b97cb15e | ||
|
|
6e40507ea8 | ||
|
|
770a487c4d | ||
|
|
dbf963445c | ||
|
|
a00bbbfd80 | ||
|
|
f3608721ae | ||
|
|
a707c4b4dd | ||
|
|
697003d1c6 | ||
|
|
570378f88c | ||
|
|
4c97c19858 | ||
|
|
cca0539c74 | ||
|
|
d9a36bacde | ||
|
|
281050e696 | ||
|
|
3abe866e01 | ||
|
|
7ba8a863b1 | ||
|
|
579eb478ed |
28
CHANGELOG.md
|
|
@ -14,6 +14,32 @@ way to update this template, but currently, we follow a pattern:
|
|||
|
||||
## Upcoming version 2019-XX-XX
|
||||
|
||||
## [v3.5.1] 2019-09-16
|
||||
|
||||
- [add] add orverriding function `onAdd` and `onRemove` for `CustomOverlayView` in
|
||||
`SearchMapWithGoogleMap` to abide to React rules and do not `unmountComponentAtNode` when a
|
||||
component is rendered by React and use `appendChild` on `onAdd` instead of `draw` to
|
||||
[improve performance](https://github.com/tomchentw/react-google-maps/issues/817).
|
||||
[#1200](https://github.com/sharetribe/flex-template-web/pull/1200)
|
||||
- [fix] fix `CustomOverlayView` in `SearchMapWithGoogleMap` to work with new `react-intl` version,
|
||||
overriding `render` method to render child object by using `createPortal` instead of
|
||||
`unstable_renderSubtreeIntoContainer`.
|
||||
[#1200](https://github.com/sharetribe/flex-template-web/pull/1200)
|
||||
|
||||
## [v3.5.0] 2019-08-29
|
||||
|
||||
- [change] Change the design of `BookingBreakdown` and add options to show only dates or booking
|
||||
date and time there. [#1195](https://github.com/sharetribe/flex-template-web/pull/1195)
|
||||
- [change] Move `BookingTimeInfo` to separate component from `InboxPage`. Add options to show only
|
||||
booking dates or booking dates and times.
|
||||
[#1194](https://github.com/sharetribe/flex-template-web/pull/1194)
|
||||
- [add] Add new Spanish translations related to storing payment card.
|
||||
[#1193](https://github.com/sharetribe/flex-template-web/pull/1193)
|
||||
- [fix] Update yarn.lock (there was Lodash version resolution missing)
|
||||
[#1190](https://github.com/sharetribe/flex-template-web/pull/1190)
|
||||
|
||||
[v3.5.0]: https://github.com/sharetribe/flex-template-web/compare/v3.4.0...v3.5.0
|
||||
|
||||
## [v3.4.0] 2019-08-29
|
||||
|
||||
- [change] Update `react-intl` to 3.1.13. More information about the changes can be found from
|
||||
|
|
@ -96,6 +122,8 @@ way to update this template, but currently, we follow a pattern:
|
|||
babel-jest, core-js, enzyme (et al.), express, helmet, inquirer, lodash, nodemon, raf, redux,
|
||||
source-map-support [#1163](https://github.com/sharetribe/flex-template-web/pull/1163)
|
||||
|
||||
[v3.4.0]: https://github.com/sharetribe/flex-template-web/compare/v3.3.0...v3.4.0
|
||||
|
||||
## [v3.3.0] 2019-08-22
|
||||
|
||||
- [add] Saving payment card after payment or without initial payment. This release contains quite a
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@ See the Flex Docs site: https://www.sharetribe.com/docs/
|
|||
|
||||
See also the [docs/](docs/) directory for some additional internal documentation.
|
||||
|
||||
## Get help – join Sharetribe Flex Developer Slack channel
|
||||
|
||||
If you have any questions about development, the best place to ask them is the Flex Developer Slack channel at https://www.sharetribe.com/flex-slack
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the terms of the Apache-2.0 license.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "app",
|
||||
"version": "3.4.0",
|
||||
"version": "3.5.1",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
|
|
|||
BIN
src/assets/background-image.jpg
Normal file
|
After Width: | Height: | Size: 362 KiB |
BIN
src/assets/colour-background.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
src/assets/ideas-image.jpg
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
src/assets/ideas.jpg
Normal file
|
After Width: | Height: | Size: 418 KiB |
BIN
src/assets/invention-process.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
src/assets/physical design.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
|
|
@ -11,20 +11,11 @@
|
|||
|
||||
.lineItem {
|
||||
@apply --marketplaceH4FontStyles;
|
||||
|
||||
/* Since the line items have different font sizes in the label and the
|
||||
value columns, we have to balance the line to align to the
|
||||
baseline. */
|
||||
margin: -1px 0 0 0;
|
||||
margin: 0;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: -1px 0 8px 0;
|
||||
|
||||
&:last-of-type {
|
||||
/* Last item should not have bottom margin that goes to the bottom of
|
||||
the component */
|
||||
margin-bottom: 0;
|
||||
}
|
||||
padding-top: 6px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
|
|
@ -32,14 +23,48 @@
|
|||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.bookingPeriod {
|
||||
flex: 1 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
padding-bottom: 6px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.bookingPeriodSectionRigth {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dayLabel {
|
||||
@apply --marketplaceH5FontStyles;
|
||||
margin: 0;
|
||||
color: var(--matterColorAnti);
|
||||
line-height: 24px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 4px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
padding-top: 1px;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.dayInfo {
|
||||
@apply --marketplaceButtonFontStyles;
|
||||
}
|
||||
|
||||
.subTotalLineItem {
|
||||
@apply --marketplaceH4FontStyles;
|
||||
font-weight: var(--fontWeightBold);
|
||||
|
||||
margin: 4px 0 1px 0;
|
||||
margin: 0;
|
||||
padding: 5px 0 1px 0;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 6px 0 9px 0;
|
||||
padding: 6px 0 2px 0;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
|
|
@ -47,72 +72,65 @@
|
|||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.lineItemTotal {
|
||||
@apply --marketplaceH4FontStyles;
|
||||
margin: 0;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
}
|
||||
.itemLabel {
|
||||
@apply --marketplaceSmallFontStyles;
|
||||
flex: 1;
|
||||
margin-top: 1px; /* align with baseline */
|
||||
}
|
||||
|
||||
.itemValue {
|
||||
@apply --marketplaceH4FontStyles;
|
||||
font-weight: 600;
|
||||
@apply --marketplaceSmallFontStyles;
|
||||
margin: 0 0 0 10px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
font-weight: 700;
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.totalDivider {
|
||||
/* dimensions */
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
margin: 13px 0 4px 0;
|
||||
margin: 12px 0 5px 0;
|
||||
|
||||
border: none;
|
||||
background-color: var(--matterColorNegative);
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 2px 0 14px 0;
|
||||
margin: 7px 0 0px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.totalLabel {
|
||||
font-weight: var(--fontWeightSemiBold);
|
||||
|
||||
/* Move so that baseline aligns with the total price */
|
||||
padding-top: 6px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
padding-top: 8px;
|
||||
}
|
||||
font-weight: var(--fontWeightNormal);
|
||||
}
|
||||
|
||||
.totalPrice {
|
||||
@apply --marketplaceBodyFontStyles;
|
||||
font-weight: var(--fontWeightSemiBold);
|
||||
@apply --marketplaceButtonFontStyles;
|
||||
margin: 0 0 0 10px;
|
||||
padding-top: 4px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 2px 0 0 10px;
|
||||
padding-top: 0;
|
||||
}
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.feeInfo {
|
||||
@apply --marketplaceTinyFontStyles;
|
||||
color: var(--matterColorAnti);
|
||||
margin: 0 0 20px 0;
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
color: var(--matterColorAnti);
|
||||
padding-top: 4px;
|
||||
padding-bottom: 14px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin-top: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media (--viewportLarge) {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 22px;
|
||||
padding-top: 11px;
|
||||
padding-bottom: 21px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,13 @@ import {
|
|||
TRANSITION_CONFIRM_PAYMENT,
|
||||
TX_TRANSITION_ACTOR_CUSTOMER,
|
||||
} from '../../util/transaction';
|
||||
import { LINE_ITEM_DAY, LINE_ITEM_NIGHT, LINE_ITEM_UNITS } from '../../util/types';
|
||||
import {
|
||||
LINE_ITEM_DAY,
|
||||
LINE_ITEM_NIGHT,
|
||||
LINE_ITEM_UNITS,
|
||||
DATE_TYPE_DATE,
|
||||
DATE_TYPE_DATETIME,
|
||||
} from '../../util/types';
|
||||
import config from '../../config';
|
||||
import BookingBreakdown from './BookingBreakdown';
|
||||
|
||||
|
|
@ -60,6 +66,7 @@ export const Checkout = {
|
|||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -86,6 +93,7 @@ export const CustomerOrder = {
|
|||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -112,6 +120,7 @@ export const ProviderSale = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(7000, CURRENCY),
|
||||
|
|
@ -145,6 +154,7 @@ export const ProviderSaleZeroCommission = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -178,6 +188,7 @@ export const ProviderSaleSingleNight = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATE,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
payoutTotal: new Money(2500, CURRENCY),
|
||||
|
|
@ -211,6 +222,7 @@ export const ProviderSalePreauthorized = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: TRANSITION_CONFIRM_PAYMENT,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -245,6 +257,7 @@ export const ProviderSaleAccepted = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: TRANSITION_ACCEPT,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -279,6 +292,7 @@ export const ProviderSaleDeclined = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: TRANSITION_DECLINE,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -313,6 +327,7 @@ export const ProviderSaleAutoDeclined = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: TRANSITION_EXPIRE,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -347,6 +362,7 @@ export const ProviderSaleDelivered = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: TRANSITION_COMPLETE,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -381,6 +397,7 @@ export const ProviderSaleCanceled = {
|
|||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: TRANSITION_CANCEL,
|
||||
payinTotal: new Money(0, CURRENCY),
|
||||
|
|
@ -432,6 +449,7 @@ export const SingleDay = {
|
|||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: LINE_ITEM_DAY,
|
||||
dateType: DATE_TYPE_DATE,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
payoutTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -458,6 +476,7 @@ export const MultipleDays = {
|
|||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: LINE_ITEM_DAY,
|
||||
dateType: DATE_TYPE_DATE,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -484,6 +503,7 @@ export const UnitsType = {
|
|||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: LINE_ITEM_UNITS,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -510,6 +530,7 @@ export const CustomPricing = {
|
|||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(12800, CURRENCY),
|
||||
payoutTotal: new Money(12600, CURRENCY),
|
||||
|
|
@ -560,6 +581,7 @@ export const CustomPricingWithRefund = {
|
|||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: DATE_TYPE_DATETIME,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(0, CURRENCY),
|
||||
payoutTotal: new Money(0, CURRENCY),
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import {
|
|||
LINE_ITEM_PROVIDER_COMMISSION,
|
||||
} from '../../util/types';
|
||||
|
||||
import LineItemUnitPriceMaybe from './LineItemUnitPriceMaybe';
|
||||
import LineItemBookingPeriod from './LineItemBookingPeriod';
|
||||
import LineItemBasePriceMaybe from './LineItemBasePriceMaybe';
|
||||
import LineItemUnitsMaybe from './LineItemUnitsMaybe';
|
||||
import LineItemSubTotalMaybe from './LineItemSubTotalMaybe';
|
||||
import LineItemCustomerCommissionMaybe from './LineItemCustomerCommissionMaybe';
|
||||
|
|
@ -27,7 +27,16 @@ import LineItemUnknownItemsMaybe from './LineItemUnknownItemsMaybe';
|
|||
import css from './BookingBreakdown.css';
|
||||
|
||||
export const BookingBreakdownComponent = props => {
|
||||
const { rootClassName, className, userRole, unitType, transaction, booking, intl } = props;
|
||||
const {
|
||||
rootClassName,
|
||||
className,
|
||||
userRole,
|
||||
unitType,
|
||||
transaction,
|
||||
booking,
|
||||
intl,
|
||||
dateType,
|
||||
} = props;
|
||||
|
||||
const isCustomer = userRole === 'customer';
|
||||
const isProvider = userRole === 'provider';
|
||||
|
|
@ -40,12 +49,49 @@ export const BookingBreakdownComponent = props => {
|
|||
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
|
||||
/**
|
||||
* BookingBreakdown contains different line items:
|
||||
*
|
||||
* LineItemBookingPeriod: prints booking start and booking end types. Prop dateType
|
||||
* determines if the date and time or only the date is shown
|
||||
*
|
||||
* LineItemUnitsMaybe: if he unitType is line-item/unit print the name and
|
||||
* quantity of the unit
|
||||
*
|
||||
* LineItemBasePriceMaybe: prints the base price calculation for the listing, e.g.
|
||||
* "$150.00 * 2 nights $300"
|
||||
*
|
||||
* LineItemUnitPriceMaybe: prints just the unit price, e.g. "Price per night $32.00".
|
||||
* This line item is not used by default in the BookingBreakdown.
|
||||
*
|
||||
* LineItemUnknownItemsMaybe: prints the line items that are unknown. In ideal case there
|
||||
* should not be unknown line items. If you are using custom pricing, you should create
|
||||
* new custom line items if you need them.
|
||||
*
|
||||
* LineItemSubTotalMaybe: prints subtotal of line items before possible
|
||||
* commission or refunds
|
||||
*
|
||||
* LineItemRefundMaybe: prints the amount of refund
|
||||
*
|
||||
* LineItemCustomerCommissionMaybe: prints the amount of customer commission
|
||||
* The default transaction process used by FTW doesn't include the customer commission.
|
||||
*
|
||||
* LineItemCustomerCommissionRefundMaybe: prints the amount of refunded customer commission
|
||||
*
|
||||
* LineItemProviderCommissionMaybe: prints the amount of provider commission
|
||||
*
|
||||
* LineItemProviderCommissionRefundMaybe: prints the amount of refunded provider commission
|
||||
*
|
||||
* LineItemTotalPrice: prints total price of the transaction
|
||||
*
|
||||
*/
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<LineItemUnitPriceMaybe transaction={transaction} unitType={unitType} intl={intl} />
|
||||
<LineItemBookingPeriod transaction={transaction} booking={booking} unitType={unitType} />
|
||||
<LineItemBookingPeriod booking={booking} unitType={unitType} dateType={dateType} />
|
||||
<LineItemUnitsMaybe transaction={transaction} unitType={unitType} />
|
||||
|
||||
<LineItemBasePriceMaybe transaction={transaction} unitType={unitType} intl={intl} />
|
||||
<LineItemUnknownItemsMaybe transaction={transaction} intl={intl} />
|
||||
|
||||
<LineItemSubTotalMaybe
|
||||
|
|
@ -78,8 +124,8 @@ export const BookingBreakdownComponent = props => {
|
|||
intl={intl}
|
||||
/>
|
||||
|
||||
<hr className={css.totalDivider} />
|
||||
<LineItemTotalPrice transaction={transaction} isProvider={isProvider} intl={intl} />
|
||||
|
||||
{hasCommissionLineItem ? (
|
||||
<span className={css.feeInfo}>
|
||||
<FormattedMessage id="BookingBreakdown.commissionFeeNote" />
|
||||
|
|
@ -89,7 +135,7 @@ export const BookingBreakdownComponent = props => {
|
|||
);
|
||||
};
|
||||
|
||||
BookingBreakdownComponent.defaultProps = { rootClassName: null, className: null };
|
||||
BookingBreakdownComponent.defaultProps = { rootClassName: null, className: null, dateType: null };
|
||||
|
||||
BookingBreakdownComponent.propTypes = {
|
||||
rootClassName: string,
|
||||
|
|
@ -99,6 +145,7 @@ BookingBreakdownComponent.propTypes = {
|
|||
unitType: propTypes.bookingUnitType.isRequired,
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
booking: propTypes.booking.isRequired,
|
||||
dateType: propTypes.dateType,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
TRANSITION_CANCEL,
|
||||
TRANSITION_REQUEST_PAYMENT,
|
||||
TX_TRANSITION_ACTOR_CUSTOMER,
|
||||
DATE_TYPE_DATE,
|
||||
} from '../../util/transaction';
|
||||
import { LINE_ITEM_NIGHT } from '../../util/types';
|
||||
import { BookingBreakdownComponent } from './BookingBreakdown';
|
||||
|
|
@ -42,6 +43,7 @@ describe('BookingBreakdown', () => {
|
|||
<BookingBreakdownComponent
|
||||
userRole="customer"
|
||||
unitType={LINE_ITEM_NIGHT}
|
||||
dateType={DATE_TYPE_DATE}
|
||||
transaction={exampleTransaction({
|
||||
payinTotal: new Money(2000, 'USD'),
|
||||
payoutTotal: new Money(2000, 'USD'),
|
||||
|
|
@ -71,6 +73,7 @@ describe('BookingBreakdown', () => {
|
|||
<BookingBreakdownComponent
|
||||
userRole="customer"
|
||||
unitType={LINE_ITEM_NIGHT}
|
||||
dateType={DATE_TYPE_DATE}
|
||||
transaction={exampleTransaction({
|
||||
payinTotal: new Money(2000, 'USD'),
|
||||
payoutTotal: new Money(2000, 'USD'),
|
||||
|
|
@ -100,6 +103,7 @@ describe('BookingBreakdown', () => {
|
|||
<BookingBreakdownComponent
|
||||
userRole="provider"
|
||||
unitType={LINE_ITEM_NIGHT}
|
||||
dateType={DATE_TYPE_DATE}
|
||||
transaction={exampleTransaction({
|
||||
payinTotal: new Money(2000, 'USD'),
|
||||
payoutTotal: new Money(1800, 'USD'),
|
||||
|
|
@ -135,6 +139,7 @@ describe('BookingBreakdown', () => {
|
|||
<BookingBreakdownComponent
|
||||
userRole="provider"
|
||||
unitType={LINE_ITEM_NIGHT}
|
||||
dateType={DATE_TYPE_DATE}
|
||||
transaction={exampleTransaction({
|
||||
lastTransition: TRANSITION_CANCEL,
|
||||
payinTotal: new Money(0, 'USD'),
|
||||
|
|
|
|||
45
src/components/BookingBreakdown/LineItemBasePriceMaybe.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage, intlShape } from '../../util/reactIntl';
|
||||
import { formatMoney } from '../../util/currency';
|
||||
import { LINE_ITEM_NIGHT, LINE_ITEM_DAY, propTypes } from '../../util/types';
|
||||
|
||||
import css from './BookingBreakdown.css';
|
||||
|
||||
const LineItemBasePriceMaybe = props => {
|
||||
const { transaction, unitType, intl } = props;
|
||||
const isNightly = unitType === LINE_ITEM_NIGHT;
|
||||
const isDaily = unitType === LINE_ITEM_DAY;
|
||||
const translationKey = isNightly
|
||||
? 'BookingBreakdown.baseUnitNight'
|
||||
: isDaily
|
||||
? 'BookingBreakdown.baseUnitDay'
|
||||
: 'BookingBreakdown.baseUnitQuantity';
|
||||
|
||||
// Find correct line-item for given unitType prop.
|
||||
// It should be one of the following: 'line-item/night, 'line-item/day', 'line-item/units', or 'line-item/time'
|
||||
// These are defined in '../../util/types';
|
||||
const unitPurchase = transaction.attributes.lineItems.find(
|
||||
item => item.code === unitType && !item.reversal
|
||||
);
|
||||
|
||||
const quantity = unitPurchase ? unitPurchase.quantity.toString() : null;
|
||||
const unitPrice = unitPurchase ? formatMoney(intl, unitPurchase.unitPrice) : null;
|
||||
const total = unitPurchase ? formatMoney(intl, unitPurchase.lineTotal) : null;
|
||||
|
||||
return quantity && total ? (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id={translationKey} values={{ unitPrice, quantity }} />
|
||||
</span>
|
||||
<span className={css.itemValue}>{total}</span>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
LineItemBasePriceMaybe.propTypes = {
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
unitType: propTypes.bookingUnitType.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default LineItemBasePriceMaybe;
|
||||
|
|
@ -1,44 +1,63 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage, FormattedHTMLMessage, FormattedDate } from '../../util/reactIntl';
|
||||
import { FormattedMessage, FormattedDate } from '../../util/reactIntl';
|
||||
import moment from 'moment';
|
||||
import { LINE_ITEM_NIGHT, LINE_ITEM_DAY, propTypes } from '../../util/types';
|
||||
import { daysBetween, dateFromAPIToLocalNoon } from '../../util/dates';
|
||||
import { LINE_ITEM_NIGHT, DATE_TYPE_DATE, propTypes } from '../../util/types';
|
||||
import { dateFromAPIToLocalNoon } from '../../util/dates';
|
||||
|
||||
import css from './BookingBreakdown.css';
|
||||
|
||||
const BookingPeriod = props => {
|
||||
const { isSingleDay, startDate, endDate } = props;
|
||||
const { startDate, endDate, dateType } = props;
|
||||
|
||||
const timeFormatOptions =
|
||||
dateType === DATE_TYPE_DATE
|
||||
? {
|
||||
weekday: 'long',
|
||||
}
|
||||
: {
|
||||
weekday: 'short',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
};
|
||||
|
||||
const dateFormatOptions = {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
};
|
||||
|
||||
if (isSingleDay) {
|
||||
return <FormattedDate value={startDate} {...dateFormatOptions} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="BookingBreakdown.bookingPeriod"
|
||||
values={{
|
||||
bookingStart: (
|
||||
<span className={css.nowrap}>
|
||||
<>
|
||||
<div className={css.bookingPeriod}>
|
||||
<div className={css.bookingPeriodSection}>
|
||||
<div className={css.dayLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.bookingStart" />
|
||||
</div>
|
||||
<div className={css.dayInfo}>
|
||||
<FormattedDate value={startDate} {...timeFormatOptions} />
|
||||
</div>
|
||||
<div className={css.itemLabel}>
|
||||
<FormattedDate value={startDate} {...dateFormatOptions} />
|
||||
</span>
|
||||
),
|
||||
bookingEnd: (
|
||||
<span className={css.nowrap}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={css.bookingPeriodSectionRigth}>
|
||||
<div className={css.dayLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.bookingEnd" />
|
||||
</div>
|
||||
<div className={css.dayInfo}>
|
||||
<FormattedDate value={endDate} {...timeFormatOptions} />
|
||||
</div>
|
||||
<div className={css.itemLabel}>
|
||||
<FormattedDate value={endDate} {...dateFormatOptions} />
|
||||
</span>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const LineItemBookingPeriod = props => {
|
||||
const { transaction, booking, unitType } = props;
|
||||
const { booking, unitType, dateType } = props;
|
||||
|
||||
// Attributes: displayStart and displayEnd can be used to differentiate shown time range
|
||||
// from actual start and end times used for availability reservation. It can help in situations
|
||||
|
|
@ -49,39 +68,22 @@ const LineItemBookingPeriod = props => {
|
|||
const localEndDateRaw = dateFromAPIToLocalNoon(displayEnd || end);
|
||||
|
||||
const isNightly = unitType === LINE_ITEM_NIGHT;
|
||||
const isDaily = unitType === LINE_ITEM_DAY;
|
||||
|
||||
const dayCount = daysBetween(localStartDate, localEndDateRaw);
|
||||
const isSingleDay = !isNightly && dayCount === 1;
|
||||
const endDay = isNightly ? localEndDateRaw : moment(localEndDateRaw).subtract(1, 'days');
|
||||
|
||||
const unitPurchase = transaction.attributes.lineItems.find(
|
||||
item => item.code === unitType && !item.reversal
|
||||
);
|
||||
|
||||
const useQuantityForDayCount = isNightly || isDaily;
|
||||
const count = useQuantityForDayCount && unitPurchase ? unitPurchase.quantity.toFixed() : dayCount;
|
||||
|
||||
const unitCountMessage = (
|
||||
<FormattedHTMLMessage
|
||||
id={isNightly ? 'BookingBreakdown.nightCount' : 'BookingBreakdown.dayCount'}
|
||||
values={{ count }}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<BookingPeriod isSingleDay={isSingleDay} startDate={localStartDate} endDate={endDay} />
|
||||
</span>
|
||||
<span className={css.itemValue}>{unitCountMessage}</span>
|
||||
</div>
|
||||
<>
|
||||
<div className={css.lineItem}>
|
||||
<BookingPeriod startDate={localStartDate} endDate={endDay} dateType={dateType} />
|
||||
</div>
|
||||
<hr className={css.totalDivider} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
LineItemBookingPeriod.defaultProps = { dateType: null };
|
||||
|
||||
LineItemBookingPeriod.propTypes = {
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
booking: propTypes.booking.isRequired,
|
||||
dateType: propTypes.dateType,
|
||||
};
|
||||
|
||||
export default LineItemBookingPeriod;
|
||||
|
|
|
|||
|
|
@ -81,12 +81,15 @@ const LineItemSubTotalMaybe = props => {
|
|||
const formattedSubTotal = subTotalLineItems.length > 0 ? formatMoney(intl, subTotal) : null;
|
||||
|
||||
return formattedSubTotal && showSubTotal ? (
|
||||
<div className={css.subTotalLineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.subTotal" />
|
||||
</span>
|
||||
<span className={css.itemValue}>{formattedSubTotal}</span>
|
||||
</div>
|
||||
<>
|
||||
<hr className={css.totalDivider} />
|
||||
<div className={css.subTotalLineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.subTotal" />
|
||||
</span>
|
||||
<span className={css.itemValue}>{formattedSubTotal}</span>
|
||||
</div>
|
||||
</>
|
||||
) : null;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,10 +31,13 @@ const LineItemUnitPrice = props => {
|
|||
const formattedTotalPrice = formatMoney(intl, totalPrice);
|
||||
|
||||
return (
|
||||
<div className={css.lineItem}>
|
||||
<div className={css.totalLabel}>{totalLabel}</div>
|
||||
<div className={css.totalPrice}>{formattedTotalPrice}</div>
|
||||
</div>
|
||||
<>
|
||||
<hr className={css.totalDivider} />
|
||||
<div className={css.lineItemTotal}>
|
||||
<div className={css.totalLabel}>{totalLabel}</div>
|
||||
<div className={css.totalPrice}>{formattedTotalPrice}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,43 +4,70 @@ exports[`BookingBreakdown customer transaction data matches snapshot 1`] = `
|
|||
<div
|
||||
className=""
|
||||
>
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.pricePerNight
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
10
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.bookingPeriod
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "BookingBreakdown.nightCount",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
Array [
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.bookingStart
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Sat 12:00 PM
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Jun 10
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.bookingEnd
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Sat 12:00 PM
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Jun 10
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<hr />,
|
||||
]
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.total
|
||||
BookingBreakdown.baseUnitNight
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
</span>
|
||||
<span>
|
||||
20
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
Array [
|
||||
<hr />,
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.total
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
20
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -48,43 +75,70 @@ exports[`BookingBreakdown pretransaction data matches snapshot 1`] = `
|
|||
<div
|
||||
className=""
|
||||
>
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.pricePerNight
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
10
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.bookingPeriod
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "BookingBreakdown.nightCount",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
Array [
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.bookingStart
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Sat 12:00 PM
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Jun 10
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.bookingEnd
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Sat 12:00 PM
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Jun 10
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<hr />,
|
||||
]
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.total
|
||||
BookingBreakdown.baseUnitNight
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
</span>
|
||||
<span>
|
||||
20
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
Array [
|
||||
<hr />,
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.total
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
20
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -92,42 +146,70 @@ exports[`BookingBreakdown provider canceled transaction data matches snapshot 1`
|
|||
<div
|
||||
className=""
|
||||
>
|
||||
Array [
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.bookingStart
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Sat 12:00 PM
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Jun 10
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.bookingEnd
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Sat 12:00 PM
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Jun 10
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<hr />,
|
||||
]
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.pricePerNight
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
10
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.bookingPeriod
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "BookingBreakdown.nightCount",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.subTotal
|
||||
BookingBreakdown.baseUnitNight
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
20
|
||||
</span>
|
||||
</div>
|
||||
Array [
|
||||
<hr />,
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.subTotal
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
20
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
|
|
@ -158,17 +240,19 @@ exports[`BookingBreakdown provider canceled transaction data matches snapshot 1`
|
|||
2
|
||||
</span>
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
Array [
|
||||
<hr />,
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.providerTotalCanceled
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
0
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.providerTotalCanceled
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
0
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.commissionFeeNote
|
||||
|
|
@ -181,42 +265,70 @@ exports[`BookingBreakdown provider transaction data matches snapshot 1`] = `
|
|||
<div
|
||||
className=""
|
||||
>
|
||||
Array [
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.bookingStart
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Sat 12:00 PM
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Jun 10
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.bookingEnd
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Sat 12:00 PM
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
Jun 10
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<hr />,
|
||||
]
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.pricePerNight
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
10
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.bookingPeriod
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "BookingBreakdown.nightCount",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.subTotal
|
||||
BookingBreakdown.baseUnitNight
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
20
|
||||
</span>
|
||||
</div>
|
||||
Array [
|
||||
<hr />,
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.subTotal
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
20
|
||||
</span>
|
||||
</div>,
|
||||
]
|
||||
<div>
|
||||
<span>
|
||||
<span>
|
||||
|
|
@ -227,17 +339,19 @@ exports[`BookingBreakdown provider transaction data matches snapshot 1`] = `
|
|||
-2
|
||||
</span>
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
Array [
|
||||
<hr />,
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.providerTotalDefault
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
18
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
BookingBreakdown.providerTotalDefault
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
18
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
<span>
|
||||
<span>
|
||||
BookingBreakdown.commissionFeeNote
|
||||
|
|
|
|||
14
src/components/BookingTimeInfo/BookingTimeInfo.css
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.root {
|
||||
}
|
||||
|
||||
.bookingInfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.dateSection {
|
||||
margin-right: 5px;
|
||||
}
|
||||
141
src/components/BookingTimeInfo/BookingTimeInfo.example.js
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import BookingTimeInfo from './BookingTimeInfo';
|
||||
import {
|
||||
fakeIntl,
|
||||
createBooking,
|
||||
createTransaction,
|
||||
createUser,
|
||||
createListing,
|
||||
} from '../../util/test-data';
|
||||
import { LINE_ITEM_DAY, LINE_ITEM_NIGHT, LINE_ITEM_UNITS } from '../../util/types';
|
||||
|
||||
export const DateAndTimeSingleDay = {
|
||||
component: BookingTimeInfo,
|
||||
props: {
|
||||
isOrder: true,
|
||||
intl: fakeIntl,
|
||||
tx: createTransaction({
|
||||
customer: createUser('user1'),
|
||||
provider: createUser('user2'),
|
||||
listing: createListing('Listing'),
|
||||
booking: createBooking('example-booking', {
|
||||
displayStart: new Date(Date.UTC(2019, 8, 30, 3, 0)),
|
||||
displayEnd: new Date(Date.UTC(2019, 8, 30, 4, 0)),
|
||||
start: new Date(Date.UTC(2019, 8, 30, 3, 0)),
|
||||
end: new Date(Date.UTC(2019, 8, 30, 4, 0)),
|
||||
}),
|
||||
}),
|
||||
unitType: LINE_ITEM_UNITS,
|
||||
dateType: 'datetime',
|
||||
},
|
||||
group: 'inbox',
|
||||
};
|
||||
|
||||
export const DateAndTimeMultipleDays = {
|
||||
component: BookingTimeInfo,
|
||||
props: {
|
||||
isOrder: true,
|
||||
intl: fakeIntl,
|
||||
tx: createTransaction({
|
||||
customer: createUser('user1'),
|
||||
provider: createUser('user2'),
|
||||
listing: createListing('Listing'),
|
||||
booking: createBooking('example-booking', {
|
||||
displayStart: new Date(Date.UTC(2019, 8, 28, 3, 0)),
|
||||
displayEnd: new Date(Date.UTC(2019, 8, 30, 5, 0)),
|
||||
start: new Date(Date.UTC(2019, 8, 28, 3, 0)),
|
||||
end: new Date(Date.UTC(2019, 8, 30, 5, 0)),
|
||||
}),
|
||||
}),
|
||||
unitType: LINE_ITEM_UNITS,
|
||||
dateType: 'datetime',
|
||||
},
|
||||
group: 'inbox',
|
||||
};
|
||||
|
||||
export const OnlyDateSingleDay = {
|
||||
component: BookingTimeInfo,
|
||||
props: {
|
||||
isOrder: true,
|
||||
intl: fakeIntl,
|
||||
tx: createTransaction({
|
||||
customer: createUser('user1'),
|
||||
provider: createUser('user2'),
|
||||
listing: createListing('Listing'),
|
||||
booking: createBooking('example-booking', {
|
||||
displayStart: new Date(Date.UTC(2019, 8, 29, 3, 0)),
|
||||
displayEnd: new Date(Date.UTC(2019, 8, 30, 4, 0)),
|
||||
start: new Date(Date.UTC(2019, 8, 29, 3, 0)),
|
||||
end: new Date(Date.UTC(2019, 8, 30, 4, 0)),
|
||||
}),
|
||||
}),
|
||||
unitType: LINE_ITEM_DAY,
|
||||
dateType: 'date',
|
||||
},
|
||||
group: 'inbox',
|
||||
};
|
||||
|
||||
export const OnlyDateMultipleDays = {
|
||||
component: BookingTimeInfo,
|
||||
props: {
|
||||
isOrder: true,
|
||||
intl: fakeIntl,
|
||||
tx: createTransaction({
|
||||
customer: createUser('user1'),
|
||||
provider: createUser('user2'),
|
||||
listing: createListing('Listing'),
|
||||
booking: createBooking('example-booking', {
|
||||
displayStart: new Date(Date.UTC(2019, 8, 28, 3, 0)),
|
||||
displayEnd: new Date(Date.UTC(2019, 8, 30, 5, 0)),
|
||||
start: new Date(Date.UTC(2019, 8, 28, 3, 0)),
|
||||
end: new Date(Date.UTC(2019, 8, 30, 5, 0)),
|
||||
}),
|
||||
}),
|
||||
unitType: LINE_ITEM_DAY,
|
||||
dateType: 'date',
|
||||
},
|
||||
group: 'inbox',
|
||||
};
|
||||
|
||||
export const OnlyDateSingleNight = {
|
||||
component: BookingTimeInfo,
|
||||
props: {
|
||||
isOrder: true,
|
||||
intl: fakeIntl,
|
||||
tx: createTransaction({
|
||||
customer: createUser('user1'),
|
||||
provider: createUser('user2'),
|
||||
listing: createListing('Listing'),
|
||||
booking: createBooking('example-booking', {
|
||||
displayStart: new Date(Date.UTC(2019, 8, 29, 3, 0)),
|
||||
displayEnd: new Date(Date.UTC(2019, 8, 30, 4, 0)),
|
||||
start: new Date(Date.UTC(2019, 8, 29, 3, 0)),
|
||||
end: new Date(Date.UTC(2019, 8, 30, 4, 0)),
|
||||
}),
|
||||
}),
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: 'date',
|
||||
},
|
||||
group: 'inbox',
|
||||
};
|
||||
|
||||
export const OnlyDateMultipleNights = {
|
||||
component: BookingTimeInfo,
|
||||
props: {
|
||||
isOrder: true,
|
||||
intl: fakeIntl,
|
||||
tx: createTransaction({
|
||||
customer: createUser('user1'),
|
||||
provider: createUser('user2'),
|
||||
listing: createListing('Listing'),
|
||||
booking: createBooking('example-booking', {
|
||||
displayStart: new Date(Date.UTC(2019, 8, 28, 3, 0)),
|
||||
displayEnd: new Date(Date.UTC(2019, 8, 30, 5, 0)),
|
||||
start: new Date(Date.UTC(2019, 8, 28, 3, 0)),
|
||||
end: new Date(Date.UTC(2019, 8, 30, 5, 0)),
|
||||
}),
|
||||
}),
|
||||
unitType: LINE_ITEM_NIGHT,
|
||||
dateType: 'date',
|
||||
},
|
||||
group: 'inbox',
|
||||
};
|
||||
98
src/components/BookingTimeInfo/BookingTimeInfo.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
import { bool } from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { txIsEnquired } from '../../util/transaction';
|
||||
import { dateFromAPIToLocalNoon, daysBetween, formatDateToText } from '../../util/dates';
|
||||
import { injectIntl, intlShape } from '../../util/reactIntl';
|
||||
import {
|
||||
LINE_ITEM_DAY,
|
||||
LINE_ITEM_NIGHT,
|
||||
LINE_ITEM_UNITS,
|
||||
DATE_TYPE_DATE,
|
||||
DATE_TYPE_DATETIME,
|
||||
propTypes,
|
||||
} from '../../util/types';
|
||||
|
||||
import css from './BookingTimeInfo.css';
|
||||
|
||||
const bookingData = (unitType, tx, isOrder, intl) => {
|
||||
// Attributes: displayStart and displayEnd can be used to differentiate shown time range
|
||||
// from actual start and end times used for availability reservation. It can help in situations
|
||||
// where there are preparation time needed between bookings.
|
||||
// Read more: https://www.sharetribe.com/api-reference/#bookings
|
||||
const { start, end, displayStart, displayEnd } = tx.booking.attributes;
|
||||
const startDate = dateFromAPIToLocalNoon(displayStart || start);
|
||||
const endDateRaw = dateFromAPIToLocalNoon(displayEnd || end);
|
||||
const isDaily = unitType === LINE_ITEM_DAY;
|
||||
const isNightly = unitType === LINE_ITEM_NIGHT;
|
||||
const isUnits = unitType === LINE_ITEM_UNITS;
|
||||
const isSingleDay = !isNightly && daysBetween(startDate, endDateRaw) <= 1;
|
||||
const bookingStart = formatDateToText(intl, startDate);
|
||||
// Shift the exclusive API end date with daily bookings
|
||||
const endDate =
|
||||
isDaily || isUnits
|
||||
? moment(endDateRaw)
|
||||
.subtract(1, 'days')
|
||||
.toDate()
|
||||
: endDateRaw;
|
||||
const bookingEnd = formatDateToText(intl, endDate);
|
||||
return { bookingStart, bookingEnd, isSingleDay };
|
||||
};
|
||||
|
||||
const BookingTimeInfoComponent = props => {
|
||||
const { bookingClassName, isOrder, intl, tx, unitType, dateType } = props;
|
||||
const isEnquiry = txIsEnquired(tx);
|
||||
|
||||
if (isEnquiry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const bookingTimes = bookingData(unitType, tx, isOrder, intl);
|
||||
|
||||
const { bookingStart, bookingEnd, isSingleDay } = bookingTimes;
|
||||
|
||||
if (isSingleDay && dateType === DATE_TYPE_DATE) {
|
||||
return (
|
||||
<div className={classNames(css.bookingInfo, bookingClassName)}>
|
||||
<span className={css.dateSection}>{`${bookingStart.date}`}</span>
|
||||
</div>
|
||||
);
|
||||
} else if (dateType === DATE_TYPE_DATE) {
|
||||
return (
|
||||
<div className={classNames(css.bookingInfo, bookingClassName)}>
|
||||
<span className={css.dateSection}>{`${bookingStart.date} -`}</span>
|
||||
<span className={css.dateSection}>{`${bookingEnd.date}`}</span>
|
||||
</div>
|
||||
);
|
||||
} else if (isSingleDay && dateType === DATE_TYPE_DATETIME) {
|
||||
return (
|
||||
<div className={classNames(css.bookingInfo, bookingClassName)}>
|
||||
<span className={css.dateSection}>
|
||||
{`${bookingStart.date}, ${bookingStart.time} - ${bookingEnd.time}`}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={classNames(css.bookingInfo, bookingClassName)}>
|
||||
<span className={css.dateSection}>{`${bookingStart.dateAndTime} - `}</span>
|
||||
<span className={css.dateSection}>{`${bookingEnd.dateAndTime}`}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
BookingTimeInfoComponent.defaultProps = { dateType: null };
|
||||
|
||||
BookingTimeInfoComponent.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
isOrder: bool.isRequired,
|
||||
tx: propTypes.transaction.isRequired,
|
||||
unitType: propTypes.bookingUnitType.isRequired,
|
||||
dateType: propTypes.dateType,
|
||||
};
|
||||
|
||||
const BookingTimeInfo = injectIntl(BookingTimeInfoComponent);
|
||||
|
||||
export default BookingTimeInfo;
|
||||
|
|
@ -10,13 +10,23 @@ const IconLogo = props => {
|
|||
{...rest}
|
||||
width="21"
|
||||
height="25"
|
||||
viewBox="0 0 21 25"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10.063 23.646c-2.286 0-4.025-1.852-4.025-4.138 0-3.342 2.405-6.49 5.69-7.246-1.875 1.382-1.77 4.06-1.032 5.176.506-.416.956-.87 1.033-1.55 1.727.68 2.358 2.147 2.358 3.733 0 2.288-1.74 4.026-4.025 4.026m9.64-12.442c-.108-.352-.536-.467-.807-.24-.7.592-1.47 1.063-2.3 1.406-.21-6.144-4.754-11.247-10.8-12.214-.42-.066-.73.385-.514.753 1.53 2.598 1.656 5.74.443 8.395-.59-.816-1.033-1.717-1.32-2.686-.1-.342-.522-.482-.805-.244C1.313 8.29 0 11.1 0 14.086c0 5.62 4.553 10.063 10.063 10.063 5.548 0 10.062-4.514 10.062-10.063 0-.982-.143-1.952-.423-2.883"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
<path d="M229.781,0c-81.569,0-147.93,63.047-147.93,140.543c0,54.028,20.928,84.706,41.178,114.388
|
||||
c16.882,24.736,34.339,50.313,35.394,84.912c0.26,8.539,6.353,15.552,14.384,17.289c-7.475,2.291-12.912,9.244-12.912,17.47
|
||||
c0,7.247,4.23,13.489,10.347,16.442c-6.116,2.954-10.347,9.196-10.347,16.444c0,10.091,8.18,18.271,18.271,18.271h11.167
|
||||
c3.45,19.216,20.24,33.801,40.448,33.801c20.208,0,37-14.585,40.449-33.801h11.166c10.092,0,18.271-8.181,18.271-18.271
|
||||
c0-7.248-4.23-13.49-10.348-16.444c6.117-2.953,10.348-9.195,10.348-16.442c0-8.189-5.389-15.12-12.812-17.44
|
||||
c8.025-1.742,14.113-8.752,14.375-17.288c1.053-34.586,18.482-60.163,35.354-84.922c20.221-29.668,41.127-60.346,41.127-114.408
|
||||
C377.71,63.047,311.351,0,229.781,0z M306.371,234.396c-15.518,22.771-34.314,50.355-40.014,86.645l-18.83-0.005l26.897-153.13
|
||||
c0.015-0.083,0.028-0.166,0.042-0.249c1.43-9.385-1.296-18.896-7.479-26.093c-5.983-6.964-14.709-11.121-23.342-11.121
|
||||
c-4.852,0-9.609,1.318-13.877,3.762c-4.269-2.444-9.024-3.762-13.877-3.762c-8.559,0-16.967,4.064-23.071,11.15
|
||||
c-6.308,7.324-9.134,16.813-7.753,26.035c0.014,0.093,0.029,0.185,0.045,0.277l26.897,153.12l-18.714-0.006
|
||||
c-5.705-36.312-24.54-63.911-40.091-86.696c-18.68-27.382-34.812-51.029-34.812-93.78c0-57.347,49.968-104.001,111.388-104.001
|
||||
c61.419,0,111.388,46.654,111.388,104.001C341.169,183.337,325.05,206.987,306.371,234.396z M229.768,293.268l-22.669-129.053
|
||||
c-0.537-3.92,1.643-6.947,2.612-8.071c1.783-2.069,4.208-3.406,6.18-3.406c2.472,0,4.369,1.851,5.28,2.953
|
||||
c2.118,2.566,5.27,4.051,8.597,4.051c3.326,0,6.479-1.485,8.597-4.051c0.912-1.103,2.809-2.953,5.281-2.953
|
||||
c2.189,0,4.653,1.286,6.43,3.354c1.133,1.319,2.936,4.115,2.365,8.103L229.768,293.268z"/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import config from '../../config';
|
||||
import IconLogo from './IconLogo';
|
||||
import LogoImage from './saunatime-logo.png';
|
||||
// import IconLogo from './IconLogo';
|
||||
import LogoImage from './inventionpad-logo.png';
|
||||
import css from './Logo.css';
|
||||
|
||||
const Logo = props => {
|
||||
|
|
@ -15,7 +15,7 @@ const Logo = props => {
|
|||
return <img className={className} src={LogoImage} alt={config.siteTitle} {...rest} />;
|
||||
}
|
||||
|
||||
return <IconLogo className={mobileClasses} {...rest} />;
|
||||
return <img src={LogoImage} width="80px" height="30px" alt={config.siteTitle} className={mobileClasses} {...rest} />;
|
||||
};
|
||||
|
||||
const { oneOf, string } = PropTypes;
|
||||
|
|
|
|||
BIN
src/components/Logo/inventionpad-logo.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
|
|
@ -1,4 +1,6 @@
|
|||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import invariant from 'invariant';
|
||||
import { arrayOf, func, node, number, oneOfType, shape, string } from 'prop-types';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { withGoogleMap, GoogleMap, OverlayView } from 'react-google-maps';
|
||||
|
|
@ -86,12 +88,46 @@ export const isMapsLibLoaded = () =>
|
|||
* https://github.com/tomchentw/react-google-maps/issues/482
|
||||
*/
|
||||
class CustomOverlayView extends OverlayView {
|
||||
onRemove() {
|
||||
this.containerElement.parentNode.removeChild(this.containerElement);
|
||||
//Remove `unmountComponentAtNode` for react version 16
|
||||
//I decided to keep the code here incase React decides not to give out warning when `unmountComponentAtNode` in newer version
|
||||
if (!React.version.match(/^16/)) {
|
||||
ReactDOM.unmountComponentAtNode(this.containerElement);
|
||||
}
|
||||
this.containerElement = null;
|
||||
}
|
||||
|
||||
onAdd() {
|
||||
this.containerElement = document.createElement(`div`);
|
||||
this.containerElement.style.position = `absolute`;
|
||||
|
||||
const { mapPaneName } = this.props;
|
||||
invariant(
|
||||
!!mapPaneName,
|
||||
`OverlayView requires either props.mapPaneName or props.defaultMapPaneName but got %s`,
|
||||
mapPaneName
|
||||
);
|
||||
|
||||
const mapPanes = this.state[OVERLAY_VIEW].getPanes();
|
||||
mapPanes[mapPaneName].appendChild(this.containerElement);
|
||||
this.onPositionElement();
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (React.version.match(/^16/) && this.containerElement) {
|
||||
return ReactDOM.createPortal(React.Children.only(this.props.children), this.containerElement);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
draw() {
|
||||
// https://developers.google.com/maps/documentation/javascript/3.exp/reference#MapPanes
|
||||
const mapPanes = this.state[OVERLAY_VIEW].getPanes();
|
||||
// Add conditional to ensure panes and container exist before drawing
|
||||
if (mapPanes && this.containerElement) {
|
||||
super.draw();
|
||||
this.onPositionElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from '../../util/reactIntl';
|
||||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import { DATE_TYPE_DATE } from '../../util/types';
|
||||
import { BookingBreakdown } from '../../components';
|
||||
|
||||
import css from './TransactionPanel.css';
|
||||
|
|
@ -16,15 +16,13 @@ const BreakdownMaybe = props => {
|
|||
|
||||
return loaded ? (
|
||||
<div className={classes}>
|
||||
<h3 className={css.bookingBreakdownTitle}>
|
||||
<FormattedMessage id="TransactionPanel.bookingBreakdownTitle" />
|
||||
</h3>
|
||||
<BookingBreakdown
|
||||
className={breakdownClasses}
|
||||
userRole={transactionRole}
|
||||
unitType={config.bookingUnitType}
|
||||
transaction={transaction}
|
||||
booking={transaction.booking}
|
||||
dateType={DATE_TYPE_DATE}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ export { default as AddImages } from './AddImages/AddImages';
|
|||
export { default as Avatar, AvatarMedium, AvatarLarge } from './Avatar/Avatar';
|
||||
export { default as BookingBreakdown } from './BookingBreakdown/BookingBreakdown';
|
||||
export { default as BookingDateRangeFilter } from './BookingDateRangeFilter/BookingDateRangeFilter';
|
||||
export { default as BookingTimeInfo } from './BookingTimeInfo/BookingTimeInfo';
|
||||
export { default as BookingPanel } from './BookingPanel/BookingPanel';
|
||||
export { default as Discussion } from './Discussion/Discussion';
|
||||
export { default as FilterPlain } from './FilterPlain/FilterPlain';
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import classNames from 'classnames';
|
|||
import config from '../../config';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { pathByRouteName, findRouteByRouteName } from '../../util/routes';
|
||||
import { propTypes, LINE_ITEM_NIGHT, LINE_ITEM_DAY } from '../../util/types';
|
||||
import { propTypes, LINE_ITEM_NIGHT, LINE_ITEM_DAY, DATE_TYPE_DATE } from '../../util/types';
|
||||
import {
|
||||
ensureListing,
|
||||
ensureCurrentUser,
|
||||
|
|
@ -586,6 +586,7 @@ export class CheckoutPageComponent extends Component {
|
|||
unitType={config.bookingUnitType}
|
||||
transaction={tx}
|
||||
booking={txBooking}
|
||||
dateType={DATE_TYPE_DATE}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
|
|
@ -769,9 +770,6 @@ export class CheckoutPageComponent extends Component {
|
|||
</div>
|
||||
|
||||
<div className={css.priceBreakdownContainer}>
|
||||
<h3 className={css.priceBreakdownTitle}>
|
||||
<FormattedMessage id="CheckoutPage.priceBreakdownTitle" />
|
||||
</h3>
|
||||
{speculateTransactionErrorMessage}
|
||||
{breakdown}
|
||||
</div>
|
||||
|
|
@ -837,9 +835,6 @@ export class CheckoutPageComponent extends Component {
|
|||
<h2 className={css.detailsTitle}>{listingTitle}</h2>
|
||||
<p className={css.detailsSubtitle}>{detailsSubTitle}</p>
|
||||
</div>
|
||||
<h3 className={css.bookingBreakdownTitle}>
|
||||
<FormattedMessage id="CheckoutPage.priceBreakdownTitle" />
|
||||
</h3>
|
||||
{speculateTransactionErrorMessage}
|
||||
{breakdown}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -77,14 +77,7 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="CheckoutPage.priceBreakdownTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</div>
|
||||
<div />
|
||||
<section>
|
||||
<injectIntl(StripePaymentForm)
|
||||
authorDisplayName="author display name"
|
||||
|
|
@ -154,12 +147,6 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
55 CheckoutPage.perNight
|
||||
</p>
|
||||
</div>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="CheckoutPage.priceBreakdownTitle"
|
||||
values={Object {}}
|
||||
/>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
|
|
|||
|
|
@ -273,29 +273,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
.bookingInfo {
|
||||
.bookingInfoWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
margin-top: 3px;
|
||||
margin-top: 2px;
|
||||
padding-top: 2px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
padding-top: 0px;
|
||||
margin-top: 8px;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.itemTimestamp {
|
||||
/* Font */
|
||||
@apply --marketplaceH5FontStyles;
|
||||
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
@media (--viewportMedium) {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.itemPrice {
|
||||
&::before {
|
||||
font-size: 10px;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { arrayOf, bool, number, oneOf, shape, string } from 'prop-types';
|
||||
import { compose } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '../../util/reactIntl';
|
||||
import moment from 'moment';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
txIsAccepted,
|
||||
|
|
@ -15,14 +14,13 @@ import {
|
|||
txIsPaymentExpired,
|
||||
txIsPaymentPending,
|
||||
} from '../../util/transaction';
|
||||
import { LINE_ITEM_DAY, LINE_ITEM_UNITS, propTypes } from '../../util/types';
|
||||
import { formatMoney } from '../../util/currency';
|
||||
import { propTypes, DATE_TYPE_DATE } from '../../util/types';
|
||||
import { ensureCurrentUser } from '../../util/data';
|
||||
import { dateFromAPIToLocalNoon, daysBetween } from '../../util/dates';
|
||||
import { getMarketplaceEntities } from '../../ducks/marketplaceData.duck';
|
||||
import { isScrollingDisabled } from '../../ducks/UI.duck';
|
||||
import {
|
||||
Avatar,
|
||||
BookingTimeInfo,
|
||||
NamedLink,
|
||||
NotificationBadge,
|
||||
Page,
|
||||
|
|
@ -43,8 +41,6 @@ import config from '../../config';
|
|||
import { loadData } from './InboxPage.duck';
|
||||
import css from './InboxPage.css';
|
||||
|
||||
const { arrayOf, bool, number, oneOf, shape, string } = PropTypes;
|
||||
|
||||
const formatDate = (intl, date) => {
|
||||
return {
|
||||
short: intl.formatDate(date, {
|
||||
|
|
@ -157,33 +153,7 @@ export const txState = (intl, tx, type) => {
|
|||
}
|
||||
};
|
||||
|
||||
const bookingData = (unitType, tx, isOrder, intl) => {
|
||||
// Attributes: displayStart and displayEnd can be used to differentiate shown time range
|
||||
// from actual start and end times used for availability reservation. It can help in situations
|
||||
// where there are preparation time needed between bookings.
|
||||
// Read more: https://www.sharetribe.com/api-reference/#bookings
|
||||
const { start, end, displayStart, displayEnd } = tx.booking.attributes;
|
||||
const startDate = dateFromAPIToLocalNoon(displayStart || start);
|
||||
const endDateRaw = dateFromAPIToLocalNoon(displayEnd || end);
|
||||
const isDaily = unitType === LINE_ITEM_DAY;
|
||||
const isUnits = unitType === LINE_ITEM_UNITS;
|
||||
const isSingleDay = isDaily && daysBetween(startDate, endDateRaw) === 1;
|
||||
const bookingStart = formatDate(intl, startDate);
|
||||
|
||||
// Shift the exclusive API end date with daily bookings
|
||||
const endDate =
|
||||
isDaily || isUnits
|
||||
? moment(endDateRaw)
|
||||
.subtract(1, 'days')
|
||||
.toDate()
|
||||
: endDateRaw;
|
||||
const bookingEnd = formatDate(intl, endDate);
|
||||
const bookingPrice = isOrder ? tx.attributes.payinTotal : tx.attributes.payoutTotal;
|
||||
const price = formatMoney(intl, bookingPrice);
|
||||
return { bookingStart, bookingEnd, price, isSingleDay };
|
||||
};
|
||||
|
||||
// Functional component as internal helper to print BookingInfo if that is needed
|
||||
// Functional component as internal helper to print BookingTimeInfo if that is needed
|
||||
const BookingInfoMaybe = props => {
|
||||
const { bookingClassName, isOrder, intl, tx, unitType } = props;
|
||||
const isEnquiry = txIsEnquired(tx);
|
||||
|
|
@ -192,13 +162,26 @@ const BookingInfoMaybe = props => {
|
|||
return null;
|
||||
}
|
||||
|
||||
const { bookingStart, bookingEnd, price, isSingleDay } = bookingData(unitType, tx, isOrder, intl);
|
||||
const dateInfo = isSingleDay ? bookingStart.short : `${bookingStart.short} - ${bookingEnd.short}`;
|
||||
// If you want to show the booking price after the booking time on InboxPage you can
|
||||
// add the price after the BookingTimeInfo component. You can get the price by uncommenting
|
||||
// sthe following lines:
|
||||
|
||||
// const bookingPrice = isOrder ? tx.attributes.payinTotal : tx.attributes.payoutTotal;
|
||||
// const price = bookingPrice ? formatMoney(intl, bookingPrice) : null;
|
||||
|
||||
// Remember to also add formatMoney function from 'util/currency.js' and add this after BookingTimeInfo:
|
||||
// <div className={css.itemPrice}>{price}</div>
|
||||
|
||||
return (
|
||||
<div className={classNames(css.bookingInfo, bookingClassName)}>
|
||||
{dateInfo}
|
||||
<span className={css.itemPrice}>{price}</span>
|
||||
<div className={classNames(css.bookingInfoWrapper, bookingClassName)}>
|
||||
<BookingTimeInfo
|
||||
bookingClassName={bookingClassName}
|
||||
isOrder={isOrder}
|
||||
intl={intl}
|
||||
tx={tx}
|
||||
unitType={unitType}
|
||||
dateType={DATE_TYPE_DATE}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -429,10 +429,16 @@ exports[`InboxPage matches snapshot 2`] = `
|
|||
<div
|
||||
className=""
|
||||
>
|
||||
2017-02-15 - 2017-02-16
|
||||
<span>
|
||||
10
|
||||
</span>
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
<span>
|
||||
Feb 15 -
|
||||
</span>
|
||||
<span>
|
||||
Feb 16
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -883,10 +889,16 @@ exports[`InboxPage matches snapshot 4`] = `
|
|||
<div
|
||||
className=""
|
||||
>
|
||||
2017-02-15 - 2017-02-16
|
||||
<span>
|
||||
9
|
||||
</span>
|
||||
<div
|
||||
className=""
|
||||
>
|
||||
<span>
|
||||
Feb 15 -
|
||||
</span>
|
||||
<span>
|
||||
Feb 16
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import * as Avatar from './components/Avatar/Avatar.example';
|
|||
import * as BookingBreakdown from './components/BookingBreakdown/BookingBreakdown.example';
|
||||
import * as BookingPanel from './components/BookingPanel/BookingPanel.example';
|
||||
import * as BookingDateRangeFilter from './components/BookingDateRangeFilter/BookingDateRangeFilter.example';
|
||||
import * as BookingTimeInfo from './components/BookingTimeInfo/BookingTimeInfo.example';
|
||||
import * as Button from './components/Button/Button.example';
|
||||
import * as ExpandingTextarea from './components/ExpandingTextarea/ExpandingTextarea.example';
|
||||
import * as FieldBirthdayInput from './components/FieldBirthdayInput/FieldBirthdayInput.example';
|
||||
|
|
@ -101,6 +102,7 @@ export {
|
|||
BookingBreakdown,
|
||||
BookingDateRangeFilter,
|
||||
BookingDatesForm,
|
||||
BookingTimeInfo,
|
||||
BookingPanel,
|
||||
Button,
|
||||
Colors,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import Decimal from 'decimal.js';
|
|||
import { types as sdkTypes } from '../../util/sdkLoader';
|
||||
import { dateFromLocalToAPI, nightsBetween, daysBetween } from '../../util/dates';
|
||||
import { TRANSITION_REQUEST_PAYMENT, TX_TRANSITION_ACTOR_CUSTOMER } from '../../util/transaction';
|
||||
import { LINE_ITEM_DAY, LINE_ITEM_NIGHT, LINE_ITEM_UNITS } from '../../util/types';
|
||||
import { LINE_ITEM_DAY, LINE_ITEM_NIGHT, LINE_ITEM_UNITS, DATE_TYPE_DATE } from '../../util/types';
|
||||
import { unitDivisor, convertMoneyToNumber, convertUnitToSubUnit } from '../../util/currency';
|
||||
import { BookingBreakdown } from '../../components';
|
||||
|
||||
|
|
@ -136,6 +136,7 @@ const EstimatedBreakdownMaybe = props => {
|
|||
unitType={unitType}
|
||||
transaction={tx}
|
||||
booking={tx.booking}
|
||||
dateType={DATE_TYPE_DATE}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
--backgroundImage: {
|
||||
/* Gradient direction and overlaying the black color on top of the image for better readability */
|
||||
background: linear-gradient(-45deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.6)),
|
||||
url('../../assets/background-1440.jpg');
|
||||
url('../../assets/ideas-image.jpg');
|
||||
|
||||
/* Add loading color for the div */
|
||||
background-color: var(--matterColor);
|
||||
|
|
@ -42,9 +42,9 @@
|
|||
|
||||
/* ================ Colors ================ */
|
||||
|
||||
--marketplaceColor: #c0392b;
|
||||
--marketplaceColorLight: #ff4c38;
|
||||
--marketplaceColorDark: #8c291e;
|
||||
--marketplaceColor: #ffbe0e;
|
||||
--marketplaceColorLight: #ffffff;
|
||||
--marketplaceColorDark: #000000;
|
||||
|
||||
/* Used with inline CSS SVGs */
|
||||
--marketplaceColorEncoded: %23c0392b;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
--marketplaceSmallFontStyles: {
|
||||
font-family: 'sofiapro', Helvetica, Arial, sans-serif;
|
||||
font-weight: var(--fontWeightMedium);
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
/* ================ Body font ================ */
|
||||
|
||||
--marketplaceBodyFontStyles: {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,11 @@
|
|||
"AuthenticationPage.verifyEmailTitle": "{name}, check your inbox to verify your email",
|
||||
"Avatar.bannedUserDisplayName": "Banned user",
|
||||
"Avatar.deletedUserDisplayName": "Deleted user",
|
||||
"BookingBreakdown.bookingPeriod": "{bookingStart} – {bookingEnd}",
|
||||
"BookingBreakdown.baseUnitDay": "{unitPrice} * {quantity, number} {quantity, plural, one {day} other {days}}",
|
||||
"BookingBreakdown.baseUnitNight": "{unitPrice} * {quantity, number} {quantity, plural, one {night} other {nights}}",
|
||||
"BookingBreakdown.baseUnitQuantity": "{unitPrice} * {quantity, number} {quantity, plural, one {person} other {persons}}",
|
||||
"BookingBreakdown.bookingEnd": "Booking end",
|
||||
"BookingBreakdown.bookingStart": "Booking start",
|
||||
"BookingBreakdown.commission": "Saunatime fee *",
|
||||
"BookingBreakdown.commissionFeeNote": "* The fee helps us run the platform and provide the best possible service to you!",
|
||||
"BookingBreakdown.dayCount": "{count, number} {count, plural, one {day} other {days}}",
|
||||
|
|
@ -142,16 +146,16 @@
|
|||
"EditListingDescriptionForm.categoryPlaceholder": "Choose the type of your sauna…",
|
||||
"EditListingDescriptionForm.categoryRequired": "You need to select a category for your sauna.",
|
||||
"EditListingDescriptionForm.createListingDraftError": "Failed to create a draft listing. Please try again.",
|
||||
"EditListingDescriptionForm.description": "Describe your sauna",
|
||||
"EditListingDescriptionForm.description": "Describe your invention",
|
||||
"EditListingDescriptionForm.descriptionPlaceholder": "How many people can fit at once? Does the sauna come with towels?",
|
||||
"EditListingDescriptionForm.descriptionRequired": "A description is required.",
|
||||
"EditListingDescriptionForm.maxLength": "Must be {maxLength} characters or less",
|
||||
"EditListingDescriptionForm.showListingFailed": "Fetching listing data failed",
|
||||
"EditListingDescriptionForm.title": "The name of your sauna",
|
||||
"EditListingDescriptionForm.titlePlaceholder": "What's the name of your sauna?",
|
||||
"EditListingDescriptionForm.title": "The name of your invention",
|
||||
"EditListingDescriptionForm.titlePlaceholder": "What's the name of your invention?",
|
||||
"EditListingDescriptionForm.titleRequired": "You need to add a name.",
|
||||
"EditListingDescriptionForm.updateFailed": "Failed to update listing. Please try again.",
|
||||
"EditListingDescriptionPanel.createListingTitle": "Add your sauna",
|
||||
"EditListingDescriptionPanel.createListingTitle": "Add your invention",
|
||||
"EditListingDescriptionPanel.title": "Edit details of {listingTitle}",
|
||||
"EditListingFeaturesForm.showListingFailed": "Fetching listing data failed",
|
||||
"EditListingFeaturesForm.updateFailed": "Failed to update listing. Please try again.",
|
||||
|
|
@ -287,7 +291,7 @@
|
|||
"Footer.toContactPage": "Contact",
|
||||
"Footer.toFAQPage": "FAQ",
|
||||
"Footer.toHelpPage": "Help",
|
||||
"Footer.toNewListingPage": "Add your sauna",
|
||||
"Footer.toNewListingPage": "Add your invention",
|
||||
"ImageCarousel.imageAltText": "Listing image {index}/{count}",
|
||||
"ImageFromFile.couldNotReadFile": "Could not read file",
|
||||
"InboxPage.fetchFailed": "Could not load all messages. Please try again.",
|
||||
|
|
@ -353,7 +357,7 @@
|
|||
"ListingPage.viewImagesButton": "View photos ({count})",
|
||||
"ListingPage.yourHostHeading": "Your host",
|
||||
"LocationAutocompleteInput.currentLocation": "Current location",
|
||||
"LocationSearchForm.placeholder": "Search saunas…",
|
||||
"LocationSearchForm.placeholder": "Search experts…",
|
||||
"LoginForm.emailInvalid": "A valid email address is required",
|
||||
"LoginForm.emailLabel": "Email",
|
||||
"LoginForm.emailPlaceholder": "john.doe@example.com",
|
||||
|
|
@ -661,6 +665,8 @@
|
|||
"ProfileSettingsForm.bioInfo": "Saunatime is built on relationships. Help other people get to know you.",
|
||||
"ProfileSettingsForm.bioLabel": "Bio",
|
||||
"ProfileSettingsForm.bioPlaceholder": "Tell us a little bit about yourself…",
|
||||
"ProfileSettingsForm.linkedinLabel": "Linkedin",
|
||||
"ProfileSettingsForm.linkedinPlaceholder": "Linkedin",
|
||||
"ProfileSettingsForm.changeAvatar": "Change",
|
||||
"ProfileSettingsForm.fileInfo": ".JPG, .GIF or .PNG. Max. 10 MB",
|
||||
"ProfileSettingsForm.firstNameLabel": "First name",
|
||||
|
|
@ -729,9 +735,9 @@
|
|||
"SearchPage.searchError": "Search failed. Please try again.",
|
||||
"SearchResultsPanel.nextPage": "Next page",
|
||||
"SearchResultsPanel.previousPage": "Previous page",
|
||||
"SectionHero.browseButton": "Browse saunas",
|
||||
"SectionHero.subTitle": "The largest online community to rent saunas in Finland.",
|
||||
"SectionHero.title": "Book saunas everywhere.",
|
||||
"SectionHero.browseButton": "Start inventing",
|
||||
"SectionHero.subTitle": "The place to take your napkin sketch all the way to manufacturing",
|
||||
"SectionHero.title": "Where ideas become REAL Products",
|
||||
"SectionHowItWorks.createListingLink": "PS. You can also become a Saunatime host in just a few clicks!",
|
||||
"SectionHowItWorks.part1Text": "Start by searching for a location. Once you find a sauna you like, simply check the availability, book it, and make a secure payment right away.",
|
||||
"SectionHowItWorks.part1Title": "1. Browse and book",
|
||||
|
|
@ -739,10 +745,10 @@
|
|||
"SectionHowItWorks.part2Title": "2. Have a great bath",
|
||||
"SectionHowItWorks.part3Text": "If you enjoyed the experience, let others know by reviewing your sauna host. Help others know where to go.",
|
||||
"SectionHowItWorks.part3Title": "3. Review the host",
|
||||
"SectionHowItWorks.titleLineOne": "The sauna ritual",
|
||||
"SectionHowItWorks.titleLineTwo": "(or how Saunatime works).",
|
||||
"SectionHowItWorks.titleLineOne": "The Invention Process",
|
||||
"SectionHowItWorks.titleLineTwo": "(or how InventionPad works).",
|
||||
"SectionLocations.listingsInLocation": "Saunas in {location}",
|
||||
"SectionLocations.title": "Explore exotic locations in Finland",
|
||||
"SectionLocations.title": "Find experts to help you",
|
||||
"SelectMultipleFilter.labelSelected": "{labelText} • {count}",
|
||||
"SelectMultipleFilterPlainForm.labelSelected": "{labelText} • {count}",
|
||||
"SelectSingleFilter.popupClear": "Clear",
|
||||
|
|
@ -877,7 +883,7 @@
|
|||
"Topbar.mobileSearchHelp": "Tip: You can also search saunas by zip code, for example \"00500\" or city district – \"Sörnäinen\".",
|
||||
"Topbar.searchIcon": "Open search",
|
||||
"TopbarDesktop.accountSettingsLink": "Account settings",
|
||||
"TopbarDesktop.createListing": "+ Add your sauna",
|
||||
"TopbarDesktop.createListing": "+ Add your invention",
|
||||
"TopbarDesktop.inbox": "Inbox",
|
||||
"TopbarDesktop.login": "Log in",
|
||||
"TopbarDesktop.logo": "Saunatime",
|
||||
|
|
@ -890,13 +896,13 @@
|
|||
"TopbarMobileMenu.inboxLink": "Inbox",
|
||||
"TopbarMobileMenu.loginLink": "Log in",
|
||||
"TopbarMobileMenu.logoutLink": "Log out",
|
||||
"TopbarMobileMenu.newListingLink": "+ Add your sauna",
|
||||
"TopbarMobileMenu.newListingLink": "+ Add your invention",
|
||||
"TopbarMobileMenu.profileSettingsLink": "Profile settings",
|
||||
"TopbarMobileMenu.signupLink": "Sign up",
|
||||
"TopbarMobileMenu.signupOrLogin": "{signup} or {login}",
|
||||
"TopbarMobileMenu.unauthorizedGreeting": "Hello there,{lineBreak}would you like to {signupOrLogin}?",
|
||||
"TopbarMobileMenu.yourListingsLink": "Your listings",
|
||||
"TopbarSearchForm.placeholder": "Search saunas…",
|
||||
"TopbarSearchForm.placeholder": "Search experts…",
|
||||
"TopbarSearchForm.searchHelp": "Tip: You can also search saunas by zip code, for example \"00500\" or city district \"Sörnäinen\".",
|
||||
"TransactionPage.deletedListing": "deleted listing",
|
||||
"TransactionPage.fetchOrderFailed": "Fetching order data failed.",
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@
|
|||
"CheckoutPage.initiateOrderError": "No se ha conseguido realizar el pago. Por favor, regresa al {listingLink} y vuelve a intentarlo. Si el problema persiste, actualiza la página o contacta al administrador del marketplace.",
|
||||
"CheckoutPage.initiateOrderStripeError": "El procesador de pago regresó los siguientes errores: {stripeErrors}",
|
||||
"CheckoutPage.listingNotFoundError": "Desafortunadamente, el anuncio ya no está disponible.",
|
||||
"CheckoutPage.loadingData": "Procesando información…",
|
||||
"CheckoutPage.paymentExpiredMessage": "El pago no se completó en 15 minutos. Por favor regresa a {listingLink} e inténtalo de nuevo.",
|
||||
"CheckoutPage.paymentInfo": "Sólo se te cobrará si el proveedor acepta la solicitud.",
|
||||
"CheckoutPage.perDay": "por día",
|
||||
|
|
@ -123,10 +122,7 @@
|
|||
"ContactDetailsForm.resendEmailVerificationText": "Reenviar correo de verificación.",
|
||||
"ContactDetailsForm.saveChanges": "Guardar cambios",
|
||||
"ContactDetailsForm.tooManyVerificationRequests": "Se han enviado demasiados correos de verificación.",
|
||||
"ContactDetailsPage.contactDetailsTabTitle": "Información de contacto",
|
||||
"ContactDetailsPage.heading": "Información de contacto",
|
||||
"ContactDetailsPage.passwordTabTitle": "Contraseña",
|
||||
"ContactDetailsPage.paymentsTabTitle": "Pagos",
|
||||
"ContactDetailsPage.title": "Información de contacto",
|
||||
"CookieConsent.continue": "Continuar",
|
||||
"CookieConsent.cookieLink": "cookies",
|
||||
|
|
@ -318,6 +314,10 @@
|
|||
"KeywordFilterPlainForm.placeholder": "Escribe una palabra clave…",
|
||||
"LandingPage.schemaDescription": "Alquila un sauna usando Saunatime or gana un dinero extra al compartir tu sauna",
|
||||
"LandingPage.schemaTitle": "Alquila saunas en cualquier parte | {siteTitle}",
|
||||
"LayoutWrapperAccountSettingsSideNav.contactDetailsTabTitle": "Información de contacto",
|
||||
"LayoutWrapperAccountSettingsSideNav.passwordTabTitle": "Contraseña",
|
||||
"LayoutWrapperAccountSettingsSideNav.paymentsTabTitle": "Configuración de pagos",
|
||||
"LayoutWrapperAccountSettingsSideNav.paymentMethodsTabTitle": "Formas de pago",
|
||||
"ListingCard.hostedBy": "Publicado por {authorName}.",
|
||||
"ListingCard.perDay": "por día",
|
||||
"ListingCard.perNight": "por noche",
|
||||
|
|
@ -426,10 +426,7 @@
|
|||
"PasswordChangeForm.passwordTooLong": "La contraseña no puede tener más de {maxLength} caracteres",
|
||||
"PasswordChangeForm.passwordTooShort": "La contraseña no puede tener menos de {minLength} caracteres",
|
||||
"PasswordChangeForm.saveChanges": "Guardar cambios",
|
||||
"PasswordChangePage.contactDetailsTabTitle": "Información de contacto",
|
||||
"PasswordChangePage.heading": "Configuración de contraseña",
|
||||
"PasswordChangePage.passwordTabTitle": "Contraseña",
|
||||
"PasswordChangePage.paymentsTabTitle": "Pagos",
|
||||
"PasswordChangePage.title": "Contraseña",
|
||||
"PasswordRecoveryForm.emailInvalid": "Se necesita una dirección de correo electrónico válida",
|
||||
"PasswordRecoveryForm.emailLabel": "Correo electrónico",
|
||||
|
|
@ -466,6 +463,19 @@
|
|||
"PasswordResetPage.recoveryLinkText": "recuperación de contraseña",
|
||||
"PasswordResetPage.resetFailed": "Ha ocurrido un error. Por favor, inténtalo de nuevo.",
|
||||
"PasswordResetPage.title": "Restablecer contraseña",
|
||||
"PaymentMethodsForm.billingDetails": "Información de facturación",
|
||||
"PaymentMethodsForm.billingDetailsNameLabel": "Nombre del titular de la tarjeta",
|
||||
"PaymentMethodsForm.billingDetailsNamePlaceholder": "Escribe el nombre…",
|
||||
"PaymentMethodsForm.paymentCardDetails": "Datos de la tarjeta de crédito",
|
||||
"PaymentMethodsForm.genericError": "Ha habido algún problema con la información de pagos. Por favor, inténtalo de nuevo.",
|
||||
"PaymentMethodsForm.handleCardPaymentError": "No hemos podido autenticar el método de pago. Por favor checa los detalles de autenticación e intenta de nuevo.",
|
||||
"PaymentMethodsForm.infoText": "Autorizo a Saunatima a mandar instrucciones a la institución bancaria que expidió mi tarjeta para cargar montos adicionales a mi cuenta de acuerdo a los términos del acuerdo con Saunatime.",
|
||||
"PaymentMethodsForm.paymentHeading": "Pago",
|
||||
"PaymentMethodsPage.heading": "Formas de pago",
|
||||
"PaymentMethodsPage.loadingData": "Cargando información…",
|
||||
"PaymentMethodsPage.savedPaymentMethodTitle": "Datos de la tarjeta de crédito",
|
||||
"PaymentMethodsForm.submitPaymentInfo": "Guardar tarjeta",
|
||||
"PaymentMethodsPage.title": "Formas de pago",
|
||||
"PayoutDetailsForm.accountOpenerInfoText": "El titular de la cuenta debe ser un individuo con autorización para firmar en nombre de la organización.",
|
||||
"PayoutDetailsForm.accountOpenerTitle": "Titular de la cuenta",
|
||||
"PayoutDetailsForm.accountTypeTitle": "Tipo de cuenta",
|
||||
|
|
@ -616,11 +626,8 @@
|
|||
"PayoutDetailsForm.stripeToSText": "Al guardar los datos, estás aceptando el {stripeConnectedAccountTermsLink}",
|
||||
"PayoutDetailsForm.submitButtonText": "Confirmar y publicar anuncio",
|
||||
"PayoutDetailsForm.title": "Un detalle más: Preferencias de pago",
|
||||
"PayoutPreferencesPage.contactDetailsTabTitle": "Información de contacto",
|
||||
"PayoutPreferencesPage.heading": "Configuración de pagos",
|
||||
"PayoutPreferencesPage.loadingData": "Cargando información…",
|
||||
"PayoutPreferencesPage.passwordTabTitle": "Contraseña",
|
||||
"PayoutPreferencesPage.paymentsTabTitle": "Pagos",
|
||||
"PayoutPreferencesPage.payoutDetailsSaved": "¡Se ha guardado tu información para recibir pagos! Si quieres modificar esta información, contacta a los administradores del marketplace.",
|
||||
"PayoutPreferencesPage.stripeAlreadyConnected": "Ya has configurado tu información para recibir pagos. Si quieres modificar esta información, contacta a los administradores del marketplace.",
|
||||
"PayoutPreferencesPage.stripeNotConnected": "No has introducido tu información para recibir pagos. Rellena el formulario para empezar a ganar dinero con tus anuncios.",
|
||||
|
|
@ -684,6 +691,16 @@
|
|||
"ReviewModal.description": "Las valoraciones son muy importantes para la comunidad de Saunatime. Por favor, comparte tu experiencia para ayudar a los demás.",
|
||||
"ReviewModal.later": "Más tarde",
|
||||
"ReviewModal.title": "Deja una valoración sobre {revieweeName}",
|
||||
"SavedCardDetails.cancel": "Cancelar",
|
||||
"SavedCardDetails.deletePaymentMethod": "Eliminar tarjeta",
|
||||
"SavedCardDetails.expiredCardText": "Esta tarjeta ha expirado",
|
||||
"SavedCardDetails.removeCard": "Eliminar tarjeta",
|
||||
"SavedCardDetails.removeCardModalContent": "La tarjeta •••• •••• •••• {last4Digits} será eliminada de tu cuenta.",
|
||||
"SavedCardDetails.removeCardModalTitle": "¿Estás seguro que quieres eliminar esta tarjeta?",
|
||||
"SavedCardDetails.replaceCardText": "Tarjeta de crédito o débito",
|
||||
"SavedCardDetails.replaceCardTitle": "Usar otro método de pago",
|
||||
"SavedCardDetails.savedPaymentMethodPlaceholderDesktop": "•••• •••• •••• {last4Digits}",
|
||||
"SavedCardDetails.savedPaymentMethodPlaceholderMobile": "…{last4Digits}",
|
||||
"SearchFilters.amenitiesLabel": "Servicios",
|
||||
"SearchFilters.categoryLabel": "Categoría",
|
||||
"SearchFilters.filtersButtonLabel": "Filtros",
|
||||
|
|
@ -822,8 +839,12 @@
|
|||
"StripePaymentForm.messageOptionalText": "• opcional",
|
||||
"StripePaymentForm.messagePlaceholder": "Hola {name}! Acabo de solicitar…",
|
||||
"StripePaymentForm.missingStripeKey": "La clave pública de Stripe no se ha configurado en este marketplace. Por esta razón, no es posible hacer reservas.",
|
||||
"StripePaymentForm.paymentCardDetails": "Datos de tarjeta de crédito",
|
||||
"StripePaymentForm.paymentCardDetails": "Datos de la tarjeta de crédito",
|
||||
"StripePaymentForm.paymentHeading": "Pago",
|
||||
"StripePaymentForm.payWithHeading": "Pagar con",
|
||||
"StripePaymentForm.replaceAfterOnetimePayment": "Reemplazar la tarjeta …{last4Digits} con la nueva tarjeta para próximas transacciones",
|
||||
"StripePaymentForm.saveAfterOnetimePayment": "Guardar tarjeta para próximas reservaciones",
|
||||
"StripePaymentForm.saveforLaterUseLegalInfo": "Autorizo a Saunatima a mandar instrucciones a la institución bancaria que expidió mi tarjeta para cargar montos adicionales a mi cuenta de acuerdo a los términos del acuerdo con Saunatime.",
|
||||
"StripePaymentForm.stripe.api_connection_error": "No se consigue conectar con API de Stripe.",
|
||||
"StripePaymentForm.stripe.api_error": "Error de conexión con Stripe.",
|
||||
"StripePaymentForm.stripe.authentication_error": "La autenticación con Stripe ha fallado.",
|
||||
|
|
@ -906,6 +927,7 @@
|
|||
"TransactionPanel.orderPreauthorizedInfo": "A {providerName} se le ha notificado tu solicitud. Te responderá pronto.",
|
||||
"TransactionPanel.orderPreauthorizedSubtitle": "Has solicitado reservar {listingLink}",
|
||||
"TransactionPanel.orderPreauthorizedTitle": "¡Bien hecho, {customerName}!",
|
||||
"TransactionPanel.paymentMethodsPageLink": "página de formas de pago",
|
||||
"TransactionPanel.perDay": "por día",
|
||||
"TransactionPanel.perNight": "por noche",
|
||||
"TransactionPanel.perUnit": "por unidad",
|
||||
|
|
@ -920,6 +942,7 @@
|
|||
"TransactionPanel.salePaymentPendingTitle": "El pago de {listingLink} no se ha confirmado",
|
||||
"TransactionPanel.saleRequestedInfo": "{customerName} está esperando tu respuesta.",
|
||||
"TransactionPanel.saleRequestedTitle": "{customerName} ha solicitado reservar {listingLink}.",
|
||||
"TransactionPanel.savePaymentMethodFailed": "No se ha podido guardar la forma de pago. Ve a {paymentMethodsPageLink} e intenta de nuevo.",
|
||||
"TransactionPanel.sendingMessageNotAllowed": "Este usuario ha sido eliminado. No es posible mandarle un mensaje.",
|
||||
"TransactionPanel.sendMessagePlaceholder": "Envía un mensaje a {name}…",
|
||||
"UserCard.contactUser": "Contactar",
|
||||
|
|
|
|||
|
|
@ -257,3 +257,20 @@ export const getExclusiveEndDate = dateString => {
|
|||
.startOf('day')
|
||||
.toDate();
|
||||
};
|
||||
|
||||
export const formatDateToText = (intl, date) => {
|
||||
return {
|
||||
date: intl.formatDate(date, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
}),
|
||||
time: intl.formatDate(date, {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
}),
|
||||
dateAndTime: intl.formatTime(date, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -496,4 +496,10 @@ propTypes.error = shape({
|
|||
apiErrors: arrayOf(propTypes.apiError),
|
||||
});
|
||||
|
||||
// Options for showing just date or date and time on BookingTimeInfo and BookingBreakdown
|
||||
export const DATE_TYPE_DATE = 'date';
|
||||
export const DATE_TYPE_DATETIME = 'datetime';
|
||||
|
||||
propTypes.dateType = oneOf([DATE_TYPE_DATE, DATE_TYPE_DATETIME]);
|
||||
|
||||
export { propTypes };
|
||||
|
|
|
|||
|
|
@ -6861,16 +6861,11 @@ lodash.uniq@^4.5.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
"lodash@>=3.5 <5", lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5:
|
||||
"lodash@>=3.5 <5", lodash@^4.1.1, lodash@^4.15.0, lodash@^4.16.2, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
|
||||
lodash@^4.1.1, lodash@^4.16.2:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
|
||||
|
||||
loglevel@^1.4.1:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.3.tgz#77f2eb64be55a404c9fd04ad16d57c1d6d6b1280"
|
||||
|
|
|
|||