mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 03:43:28 +10:00
Merge pull request #868 from sharetribe/fetch-availability-data
Fetch availability data
This commit is contained in:
commit
0d14e447bd
8 changed files with 96 additions and 12 deletions
|
|
@ -48,7 +48,7 @@
|
|||
"redux-thunk": "^2.2.0",
|
||||
"sanitize.css": "^5.0.0",
|
||||
"sharetribe-scripts": "1.1.2",
|
||||
"sharetribe-sdk": "https://github.com/sharetribe/sharetribe-sdk-js.git#b438be11d0f40f62e1a0388da745f6792420ddb1",
|
||||
"sharetribe-sdk": "https://github.com/sharetribe/sharetribe-sdk-js.git#b7ffe0fe0a5bdb0a1471e2ff36cdd25b997fb39b",
|
||||
"smoothscroll-polyfill": "^0.4.0",
|
||||
"source-map-support": "^0.5.4",
|
||||
"url": "^0.11.0"
|
||||
|
|
|
|||
|
|
@ -75,11 +75,9 @@ const defaultProps = {
|
|||
enableOutsideDays: false,
|
||||
isDayBlocked: () => false,
|
||||
|
||||
// Stripe holds funds in a reserve for up to 90 days from charge creation.
|
||||
// outside range -><- today ... today+89 days -><- outside range
|
||||
// outside range -><- today ... today+available days -1 -><- outside range
|
||||
isOutsideRange: day => {
|
||||
const daysCountAvailableToBook = 90;
|
||||
const endOfRange = daysCountAvailableToBook - 1;
|
||||
const endOfRange = config.dayCountAvailableForBooking - 1;
|
||||
return (
|
||||
!isInclusivelyAfterDay(day, moment()) ||
|
||||
!isInclusivelyBeforeDay(day, moment().add(endOfRange, 'days'))
|
||||
|
|
|
|||
|
|
@ -117,11 +117,9 @@ const defaultProps = {
|
|||
enableOutsideDays: false,
|
||||
isDayBlocked: () => false,
|
||||
|
||||
// Stripe holds funds in a reserve for up to 90 days from charge creation.
|
||||
// outside range -><- today ... today+89 days -><- outside range
|
||||
// outside range -><- today ... today+available days -1 -><- outside range
|
||||
isOutsideRange: day => {
|
||||
const daysCountAvailableToBook = 90;
|
||||
const endOfRange = daysCountAvailableToBook - 1;
|
||||
const endOfRange = config.dayCountAvailableForBooking - 1;
|
||||
return (
|
||||
!isInclusivelyAfterDay(day, moment()) ||
|
||||
!isInclusivelyBeforeDay(day, moment().add(endOfRange, 'days'))
|
||||
|
|
|
|||
|
|
@ -36,6 +36,15 @@ const bookingProcessAlias = 'preauth-with-nightly-booking/release-1';
|
|||
// translations when the unit is changed.
|
||||
const bookingUnitType = 'line-item/night';
|
||||
|
||||
// Should the application fetch available time slots (currently defined as
|
||||
// start and end dates) to be shown on listing page.
|
||||
const fetchAvailableTimeSlots = false;
|
||||
|
||||
// A maximum number of days forwards during which a booking can be made.
|
||||
// This is limited due to Stripe holding funds up to 90 days from the
|
||||
// moment they are charged.
|
||||
const dayCountAvailableForBooking = 90;
|
||||
|
||||
// To pass environment variables to the client app in the build
|
||||
// script, react-scripts (and the sharetribe-scripts fork of
|
||||
// react-scripts) require using the REACT_APP_ prefix to avoid
|
||||
|
|
@ -313,6 +322,8 @@ const config = {
|
|||
locale,
|
||||
bookingProcessAlias,
|
||||
bookingUnitType,
|
||||
fetchAvailableTimeSlots,
|
||||
dayCountAvailableForBooking,
|
||||
i18n,
|
||||
sdk: {
|
||||
clientId: sdkClientId,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import pick from 'lodash/pick';
|
||||
import moment from 'moment';
|
||||
import config from '../../config';
|
||||
import { types as sdkTypes } from '../../util/sdkLoader';
|
||||
import { storableError } from '../../util/errors';
|
||||
|
|
@ -21,6 +22,10 @@ export const FETCH_REVIEWS_REQUEST = 'app/ListingPage/FETCH_REVIEWS_REQUEST';
|
|||
export const FETCH_REVIEWS_SUCCESS = 'app/ListingPage/FETCH_REVIEWS_SUCCESS';
|
||||
export const FETCH_REVIEWS_ERROR = 'app/ListingPage/FETCH_REVIEWS_ERROR';
|
||||
|
||||
export const FETCH_TIME_SLOTS_REQUEST = 'app/ListingPage/FETCH_TIME_SLOTS_REQUEST';
|
||||
export const FETCH_TIME_SLOTS_SUCCESS = 'app/ListingPage/FETCH_TIME_SLOTS_SUCCESS';
|
||||
export const FETCH_TIME_SLOTS_ERROR = 'app/ListingPage/FETCH_TIME_SLOTS_ERROR';
|
||||
|
||||
export const SEND_ENQUIRY_REQUEST = 'app/ListingPage/SEND_ENQUIRY_REQUEST';
|
||||
export const SEND_ENQUIRY_SUCCESS = 'app/ListingPage/SEND_ENQUIRY_SUCCESS';
|
||||
export const SEND_ENQUIRY_ERROR = 'app/ListingPage/SEND_ENQUIRY_ERROR';
|
||||
|
|
@ -32,6 +37,8 @@ const initialState = {
|
|||
showListingError: null,
|
||||
reviews: [],
|
||||
fetchReviewsError: null,
|
||||
timeSlots: [],
|
||||
fetchTimesLotsError: null,
|
||||
sendEnquiryInProgress: false,
|
||||
sendEnquiryError: null,
|
||||
enquiryModalOpenForListingId: null,
|
||||
|
|
@ -55,6 +62,13 @@ const listingPageReducer = (state = initialState, action = {}) => {
|
|||
case FETCH_REVIEWS_ERROR:
|
||||
return { ...state, fetchReviewsError: payload };
|
||||
|
||||
case FETCH_TIME_SLOTS_REQUEST:
|
||||
return { ...state, fetchTimeSlotsError: null };
|
||||
case FETCH_TIME_SLOTS_SUCCESS:
|
||||
return { ...state, timeSlots: payload };
|
||||
case FETCH_TIME_SLOTS_ERROR:
|
||||
return { ...state, fetchTimeSlotsError: payload };
|
||||
|
||||
case SEND_ENQUIRY_REQUEST:
|
||||
return { ...state, sendEnquiryInProgress: true, sendEnquiryError: null };
|
||||
case SEND_ENQUIRY_SUCCESS:
|
||||
|
|
@ -95,6 +109,17 @@ export const fetchReviewsError = error => ({
|
|||
payload: error,
|
||||
});
|
||||
|
||||
export const fetchTimeSlotsRequest = () => ({ type: FETCH_TIME_SLOTS_REQUEST });
|
||||
export const fetchTimeSlotsSuccess = timeSlots => ({
|
||||
type: FETCH_TIME_SLOTS_SUCCESS,
|
||||
payload: timeSlots,
|
||||
});
|
||||
export const fetchTimeSlotsError = error => ({
|
||||
type: FETCH_TIME_SLOTS_ERROR,
|
||||
error: true,
|
||||
payload: error,
|
||||
});
|
||||
|
||||
export const sendEnquiryRequest = () => ({ type: SEND_ENQUIRY_REQUEST });
|
||||
export const sendEnquirySuccess = () => ({ type: SEND_ENQUIRY_SUCCESS });
|
||||
export const sendEnquiryError = e => ({ type: SEND_ENQUIRY_ERROR, error: true, payload: e });
|
||||
|
|
@ -143,6 +168,7 @@ export const showListing = (listingId, isOwn = false) => (dispatch, getState, sd
|
|||
};
|
||||
|
||||
export const fetchReviews = listingId => (dispatch, getState, sdk) => {
|
||||
dispatch(fetchReviewsRequest);
|
||||
return sdk.reviews
|
||||
.query({
|
||||
listing_id: listingId,
|
||||
|
|
@ -159,6 +185,19 @@ export const fetchReviews = listingId => (dispatch, getState, sdk) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const fetchTimeSlots = params => (dispatch, getState, sdk) => {
|
||||
dispatch(fetchTimeSlotsRequest);
|
||||
return sdk.timeslots
|
||||
.query(params)
|
||||
.then(response => {
|
||||
const timeSlots = denormalisedResponseEntities(response);
|
||||
dispatch(fetchTimeSlotsSuccess(timeSlots));
|
||||
})
|
||||
.catch(e => {
|
||||
dispatch(fetchTimeSlotsError(storableError(e)));
|
||||
});
|
||||
};
|
||||
|
||||
export const sendEnquiry = (listingId, message) => (dispatch, getState, sdk) => {
|
||||
dispatch(sendEnquiryRequest());
|
||||
const bodyParams = {
|
||||
|
|
@ -190,5 +229,23 @@ export const loadData = (params, search) => dispatch => {
|
|||
if (params.variant === LISTING_PAGE_PENDING_APPROVAL_VARIANT) {
|
||||
return dispatch(showListing(listingId, true));
|
||||
}
|
||||
return Promise.all([dispatch(showListing(listingId)), dispatch(fetchReviews(listingId))]);
|
||||
|
||||
if (config.fetchAvailableTimeSlots) {
|
||||
// fetch time slots for 90 days starting today
|
||||
// as the booking can only be done for 90 days
|
||||
// in the future due to Stripe limitations
|
||||
const start = moment().toDate();
|
||||
const end = moment()
|
||||
.add(config.dayCountAvailableForBooking, 'days')
|
||||
.toDate();
|
||||
const timeSlotsParams = { listingId, start, end };
|
||||
|
||||
return Promise.all([
|
||||
dispatch(showListing(listingId)),
|
||||
dispatch(fetchTimeSlots(timeSlotsParams)),
|
||||
dispatch(fetchReviews(listingId)),
|
||||
]);
|
||||
} else {
|
||||
return Promise.all([dispatch(showListing(listingId)), dispatch(fetchReviews(listingId))]);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -497,6 +497,8 @@ ListingPageComponent.defaultProps = {
|
|||
showListingError: null,
|
||||
reviews: [],
|
||||
fetchReviewsError: null,
|
||||
timeSlots: [],
|
||||
fetchTimeSlotsError: null,
|
||||
sendEnquiryError: null,
|
||||
categoriesConfig: config.custom.categories,
|
||||
amenitiesConfig: config.custom.amenities,
|
||||
|
|
@ -532,6 +534,8 @@ ListingPageComponent.propTypes = {
|
|||
useInitialValues: func.isRequired,
|
||||
reviews: arrayOf(propTypes.review),
|
||||
fetchReviewsError: propTypes.error,
|
||||
timeSlots: arrayOf(propTypes.timeSlot),
|
||||
fetchTimeSlotsError: propTypes.error,
|
||||
sendEnquiryInProgress: bool.isRequired,
|
||||
sendEnquiryError: propTypes.error,
|
||||
onSendEnquiry: func.isRequired,
|
||||
|
|
@ -546,6 +550,8 @@ const mapStateToProps = state => {
|
|||
showListingError,
|
||||
reviews,
|
||||
fetchReviewsError,
|
||||
timeSlots,
|
||||
fetchTimeSlotsError,
|
||||
sendEnquiryInProgress,
|
||||
sendEnquiryError,
|
||||
enquiryModalOpenForListingId,
|
||||
|
|
@ -574,6 +580,8 @@ const mapStateToProps = state => {
|
|||
showListingError,
|
||||
reviews,
|
||||
fetchReviewsError,
|
||||
timeSlots,
|
||||
fetchTimeSlotsError,
|
||||
sendEnquiryInProgress,
|
||||
sendEnquiryError,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -187,6 +187,16 @@ propTypes.booking = shape({
|
|||
}),
|
||||
});
|
||||
|
||||
// Denormalised time slot object
|
||||
propTypes.timeSlot = shape({
|
||||
id: propTypes.uuid.isRequired,
|
||||
type: propTypes.value('timeSlot').isRequired,
|
||||
attributes: shape({
|
||||
end: instanceOf(Date).isRequired,
|
||||
start: instanceOf(Date).isRequired,
|
||||
}),
|
||||
});
|
||||
|
||||
// When a customer makes a booking to a listing, a transaction is
|
||||
// created with the initial request transition.
|
||||
export const TRANSITION_REQUEST = 'transition/request';
|
||||
|
|
@ -425,6 +435,7 @@ export const ERROR_CODE_EMAIL_NOT_VERIFIED = 'email-unverified';
|
|||
export const ERROR_CODE_TOO_MANY_VERIFICATION_REQUESTS = 'email-too-many-verification-requests';
|
||||
export const ERROR_CODE_UPLOAD_OVER_LIMIT = 'request-upload-over-limit';
|
||||
export const ERROR_CODE_VALIDATION_INVALID_PARAMS = 'validation-invalid-params';
|
||||
export const ERROR_CODE_VALIDATION_INVALID_VALUE = 'validation-invalid-value';
|
||||
export const ERROR_CODE_NOT_FOUND = 'not-found';
|
||||
export const ERROR_CODE_FORBIDDEN = 'forbidden';
|
||||
export const ERROR_CODE_MISSING_STRIPE_ACCOUNT = 'transaction-missing-stripe-account';
|
||||
|
|
@ -441,6 +452,7 @@ const ERROR_CODES = [
|
|||
ERROR_CODE_TOO_MANY_VERIFICATION_REQUESTS,
|
||||
ERROR_CODE_UPLOAD_OVER_LIMIT,
|
||||
ERROR_CODE_VALIDATION_INVALID_PARAMS,
|
||||
ERROR_CODE_VALIDATION_INVALID_VALUE,
|
||||
ERROR_CODE_NOT_FOUND,
|
||||
ERROR_CODE_FORBIDDEN,
|
||||
ERROR_CODE_MISSING_STRIPE_ACCOUNT,
|
||||
|
|
|
|||
|
|
@ -7574,9 +7574,9 @@ sharetribe-scripts@1.1.2:
|
|||
optionalDependencies:
|
||||
fsevents "^1.1.3"
|
||||
|
||||
"sharetribe-sdk@https://github.com/sharetribe/sharetribe-sdk-js.git#b438be11d0f40f62e1a0388da745f6792420ddb1":
|
||||
"sharetribe-sdk@https://github.com/sharetribe/sharetribe-sdk-js.git#b7ffe0fe0a5bdb0a1471e2ff36cdd25b997fb39b":
|
||||
version "0.0.1"
|
||||
resolved "https://github.com/sharetribe/sharetribe-sdk-js.git#b438be11d0f40f62e1a0388da745f6792420ddb1"
|
||||
resolved "https://github.com/sharetribe/sharetribe-sdk-js.git#b7ffe0fe0a5bdb0a1471e2ff36cdd25b997fb39b"
|
||||
dependencies:
|
||||
axios "^0.15.3"
|
||||
js-cookie "^2.1.3"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue