mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Merge pull request #789 from sharetribe/listing-minimum-price
Add support for configuring a minimum price for a listing
This commit is contained in:
commit
228a4d716d
5 changed files with 68 additions and 6 deletions
|
|
@ -37,6 +37,10 @@ const sdkBaseUrl = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL;
|
|||
|
||||
const currency = process.env.REACT_APP_SHARETRIBE_MARKETPLACE_CURRENCY;
|
||||
|
||||
// Listing minimum price in currency sub units, e.g. cents.
|
||||
// 0 means no restriction to the price
|
||||
const listingMinimumPriceSubUnits = 0;
|
||||
|
||||
// Sentry DSN (Data Source Name), a client key for authenticating calls to Sentry
|
||||
const sentryDsn = process.env.REACT_APP_PUBLIC_SENTRY_DSN;
|
||||
|
||||
|
|
@ -283,6 +287,7 @@ const config = {
|
|||
sdk: { clientId: sdkClientId, baseUrl: sdkBaseUrl },
|
||||
sortSearchByDistance,
|
||||
currency,
|
||||
listingMinimumPriceSubUnits,
|
||||
currencyConfig,
|
||||
stripe: { publishableKey: stripePublishableKey, supportedCountries: stripeSupportedCountries },
|
||||
canonicalRootURL,
|
||||
|
|
|
|||
|
|
@ -6,11 +6,15 @@ import { intlShape, injectIntl, FormattedMessage } from 'react-intl';
|
|||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import { propTypes } from '../../util/types';
|
||||
import { required } from '../../util/validators';
|
||||
import * as validators from '../../util/validators';
|
||||
import { formatMoney } from '../../util/currency';
|
||||
import { types as sdkTypes } from '../../util/sdkLoader';
|
||||
import { Form, Button, FieldCurrencyInput } from '../../components';
|
||||
|
||||
import css from './EditListingPricingForm.css';
|
||||
|
||||
const { Money } = sdkTypes;
|
||||
|
||||
export const EditListingPricingFormComponent = props => {
|
||||
const {
|
||||
className,
|
||||
|
|
@ -26,11 +30,31 @@ export const EditListingPricingFormComponent = props => {
|
|||
} = props;
|
||||
|
||||
const pricePerUnitMessage = intl.formatMessage({ id: 'EditListingPricingForm.pricePerUnit' });
|
||||
const priceRequiredMessage = intl.formatMessage({ id: 'EditListingPricingForm.priceRequired' });
|
||||
const pricePlaceholderMessage = intl.formatMessage({
|
||||
id: 'EditListingPricingForm.priceInputPlaceholder',
|
||||
});
|
||||
|
||||
const priceRequired = validators.required(
|
||||
intl.formatMessage({
|
||||
id: 'EditListingPricingForm.priceRequired',
|
||||
})
|
||||
);
|
||||
const minPrice = new Money(config.listingMinimumPriceSubUnits, config.currency);
|
||||
const minPriceRequired = validators.moneySubUnitAmountAtLeast(
|
||||
intl.formatMessage(
|
||||
{
|
||||
id: 'EditListingPricingForm.priceTooLow',
|
||||
},
|
||||
{
|
||||
minPrice: formatMoney(intl, minPrice),
|
||||
}
|
||||
),
|
||||
config.listingMinimumPriceSubUnits
|
||||
);
|
||||
const priceValidators = config.listingMinimumPriceSubUnits
|
||||
? [priceRequired, minPriceRequired]
|
||||
: [priceRequired];
|
||||
|
||||
const errorMessage = updateError ? (
|
||||
<p className={css.error}>
|
||||
<FormattedMessage id="EditListingPricingForm.updateFailed" />
|
||||
|
|
@ -53,7 +77,7 @@ export const EditListingPricingFormComponent = props => {
|
|||
label={pricePerUnitMessage}
|
||||
placeholder={pricePlaceholderMessage}
|
||||
currencyConfig={config.currencyConfig}
|
||||
validate={[required(priceRequiredMessage)]}
|
||||
validate={priceValidators}
|
||||
/>
|
||||
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@
|
|||
"EditListingPricingForm.priceInputPlaceholder": "Choose your price…",
|
||||
"EditListingPricingForm.pricePerUnit": "Price per night in euros",
|
||||
"EditListingPricingForm.priceRequired": "You need to add a valid price.",
|
||||
"EditListingPricingForm.priceTooLow": "Price should be at least {minPrice}.",
|
||||
"EditListingPricingForm.updateFailed": "Failed to update listing. Please try again.",
|
||||
"EditListingPricingPanel.createListingTitle": "How much does it cost?",
|
||||
"EditListingPricingPanel.listingPriceCurrencyInvalid": "Listing currency is different from the marketplace currency. You cannot edit the price.",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import moment from 'moment';
|
||||
import { types } from 'sharetribe-sdk';
|
||||
import { types as sdkTypes } from './sdkLoader';
|
||||
import { toPairs } from 'lodash';
|
||||
|
||||
const { LatLng } = types;
|
||||
const { LatLng, Money } = sdkTypes;
|
||||
|
||||
export const PASSWORD_MIN_LENGTH = 8;
|
||||
export const PASSWORD_MAX_LENGTH = 256;
|
||||
|
|
@ -110,6 +110,10 @@ export const emailFormatValid = message => value => {
|
|||
return value && EMAIL_RE.test(value) ? VALID : message;
|
||||
};
|
||||
|
||||
export const moneySubUnitAmountAtLeast = (message, minValue) => value => {
|
||||
return value instanceof Money && value.amount >= minValue ? VALID : message;
|
||||
};
|
||||
|
||||
export const ageAtLeast = (message, minYears) => value => {
|
||||
const now = moment();
|
||||
const ageInYears = now.diff(moment(value), 'years', true);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
import { required, requiredStringNoTrim, minLength, maxLength } from './validators';
|
||||
import { types as sdkTypes } from './sdkLoader';
|
||||
import {
|
||||
required,
|
||||
requiredStringNoTrim,
|
||||
minLength,
|
||||
maxLength,
|
||||
moneySubUnitAmountAtLeast,
|
||||
} from './validators';
|
||||
|
||||
const { Money } = sdkTypes;
|
||||
|
||||
describe('validators', () => {
|
||||
describe('required()', () => {
|
||||
|
|
@ -120,4 +129,23 @@ describe('validators', () => {
|
|||
expect(maxLength('fail', 3)([1, 2, 3, 4])).toEqual('fail');
|
||||
});
|
||||
});
|
||||
describe('moneySubUnitAmountAtLeast()', () => {
|
||||
it('should not allow empty or missing value', () => {
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(undefined)).toEqual('fail');
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(null)).toEqual('fail');
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)('')).toEqual('fail');
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(0)).toEqual('fail');
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(50)).toEqual('fail');
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(100)).toEqual('fail');
|
||||
});
|
||||
it('should not allow too low values', () => {
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(new Money(0, 'USD'))).toEqual('fail');
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(new Money(49, 'USD'))).toEqual('fail');
|
||||
});
|
||||
it('should allow large enough values', () => {
|
||||
expect(moneySubUnitAmountAtLeast('fail', 0)(new Money(0, 'USD'))).toBeUndefined();
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(new Money(50, 'USD'))).toBeUndefined();
|
||||
expect(moneySubUnitAmountAtLeast('fail', 50)(new Money(100, 'USD'))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue