{
    "componentChunkName": "component---src-templates-article-page-template-js",
    "path": "/how-to/bookings-with-buffer/",
    "result": {"data":{"markdownRemark":{"frontmatter":{"title":"Add buffer time to bookings in FTW-hourly","slug":"bookings-with-buffer","updated":"2022-09-13T00:00:00.000Z","category":"how-to-listing","ingress":"This guide describes how to modify booking times in FTW-hourly to have a buffer after the time slots","skills":null},"htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"For some services booked by the hour, the provider may want to reserve a\nbuffer time between bookings. For instance a yoga teacher may want to\nhave a break between private customers, or a massage therapist may need\nto clear up and restock their therapy space before the next customer\narrives. You can set your marketplace to add these kinds of buffers by\ndefault, so providers do not need to add availability exceptions\nmanually."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{"id":"add-a-15-minute-buffer-between-one-hour-bookings","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#add-a-15-minute-buffer-between-one-hour-bookings","ariaLabel":"add a 15 minute buffer between one hour bookings permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Add a 15 minute buffer between one hour bookings"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The simplest use case for buffered bookings is having one hour slots\nthat are bookable at the top of the hour, and adding 15 minutes\nunbookable time after each slot. To achieve that, we will use the\nbooking's display time attribute."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"First, we will add a helper function in "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"src/util/dates.js"}]},{"type":"text","value":" file to add\nthe correct buffer time."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"└── src\n    └── util\n        └── dates.js"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" const bufferMinutes = 15;\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" export const addBuffer = (date) => moment(date).add(bufferMinutes, 'minutes').toDate();"}]}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"add-display-end-time-handling","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#add-display-end-time-handling","ariaLabel":"add display end time handling permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Add display end time handling"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The user can select one hour booking slots on the listing page. When an\norder gets initiated, we want to set the selected end time as the\ndisplay time, and a new buffered end time as the actual booking time.\nThis way, the booking extends over both the customer's booking time and\nthe buffer, so that other customers can not book over the buffer. We use\nthe newly created "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"addBuffer"}]},{"type":"text","value":" function to extend the display end time."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"└── src\n    └── containers\n        └── CheckoutPage\n            └── CheckoutPage.duck.js"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" import { addBuffer } from '../../util/dates';\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"...\n"}]},{"type":"text","value":"\nexport const initiateOrder = (orderParams, transactionId) => (dispatch, getState, sdk) => {\n"},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"  dispatch(initiateOrderRequest());\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"...\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"  const bufferEnd = addBuffer(orderParams.bookingEnd);\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"  const bufferedParams = {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"    ...orderParams,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"    bookingEnd: bufferEnd,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"    bookingDisplayEnd: orderParams.bookingEnd,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"    bookingDisplayStart: orderParams.bookingStart,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"  };\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"  const bookingData = {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"    startDate: orderParams.bookingStart,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"    endDate: orderParams.bookingEnd,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"    endDate: bufferedParams.bookingEnd,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"    displayEnd: bufferedParams.bookingDisplayEnd,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"  };\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"  const bodyParams = isTransition\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"    ? {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"        id: transactionId,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"        transition,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"        params: orderParams,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"        params: bufferedParams,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"      }\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"    : {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"        processAlias: config.bookingProcessAlias,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"        transition,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"        params: orderParams,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"        params: bufferedParams,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"      };"}]}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We also need to modify line item calculation. By default, the\ntransaction's price is calculated based on the booking's start and end\nmoments, however in this case we want to use the display end attribute\nfor the price calculation."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"└── server\n    └── api-util\n        └── lineItems.js"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We have already passed "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"displayEnd"}]},{"type":"text","value":" in "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"bookingData"}]},{"type":"text","value":", so we just need to\nadd handling for it in "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"lineItems.js"}]},{"type":"text","value":". Since the function is used both\nwith and without display end time, e.g. when calling\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"api/transaction-line-items"}]},{"type":"text","value":", we need to accommodate both use cases."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"text","value":"exports.transactionLineItems = (listing, bookingData) => {\n"},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" const unitPrice = listing.attributes.price;\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" const { startDate, endDate } = bookingData;\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" const { startDate, endDate, displayEnd } = bookingData;\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"...\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" const booking = {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   code: bookingUnitType,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   unitPrice,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"   quantity: calculateQuantityFromHours(startDate, endDate),\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   quantity: calculateQuantityFromHours(startDate, (displayEnd || endDate)),\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   includeFor: ['customer', 'provider'],\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" };"}]}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"set-getstarthours-to-return-correct-available-start-times","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#set-getstarthours-to-return-correct-available-start-times","ariaLabel":"set getstarthours to return correct available start times permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Set getStartHours to return correct available start times"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Finally, we need to make sure that the start time slots only show valid\nstart times including the buffer – if someone has booked 6pm to 7pm,\nthen another customer cannot book 5pm to 6pm because there is not enough\navailability for both the time slot and the buffer."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"└── src\n    └── util\n        └── dates.js"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"First, add a constant for a full hour in addition to the buffer time."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" const bufferMinutes = 15;\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" const hourMinutes = 60;"}]}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Then, fix "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getStartHours"}]},{"type":"text","value":" handling. By default, start hours and end\nhours are determined from the same list, so "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getStartHours"}]},{"type":"text","value":" removes the\nfinal list item to allow for a single hour between the final start and\nend times."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"However, when the actual booking slot is longer than the displayed one,\nwe need to make sure that the interval between the final start and end\ntimes can fit a buffered booking. Therefore, instead of removing a\nsingle start time to fit a one hour booking slot, we remove as many\nstart times as it takes to fit a buffered hour before the final end\ntime."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"text","value":"export const getStartHours = (intl, timeZone, startTime, endTime) => {\n"},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" const hours = getSharpHours(intl, timeZone, startTime, endTime);\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" return hours.length < 2 ? hours : hours.slice(0, -1);\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" const removeCount = Math.ceil((hourMinutes + bufferMinutes) / hourMinutes);\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" return hours.length < removeCount ? [] : hours.slice(0, -removeCount);\n"}]},{"type":"text","value":"};"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{"id":"allow-users-to-book-buffered-appointments-at-non-sharp-hours","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#allow-users-to-book-buffered-appointments-at-non-sharp-hours","ariaLabel":"allow users to book buffered appointments at non sharp hours permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Allow users to book buffered appointments at non-sharp hours"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Having this kind of setup then means that the slots are, in practice,\nbookable every two hours. If we do not want to leave extra gaps between\nbookings beyond the buffer, we can set start times to repeat on the\nbuffer time cadence instead of hourly. That way, if someone books the 8\nAM - 9 AM slot, the next available start time is 9.15 AM i.e. right\nafter the buffer."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In other words, we need to have start and end times at a different\ncycle:"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"start times at 15 minute increments i.e. customer can start their\nbooking at any quarter hour, instead of at the top of the hour only"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"end times at one hour increments, i.e. the customer can book one or\nmore full hours only."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Time slot handling is done using a few helper functions in\nsrc/util/dates.js"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"└── src\n    └── util\n        └── dates.js"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getStartHours"}]},{"type":"text","value":" and "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getEndHours"}]},{"type":"text","value":" return a list of timepoints that are\ndisplayed as the booking's possible start and end moments,\nrespectively. They both use the same helper function "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getSharpHours"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getSharpHours"}]},{"type":"text","value":", in turn, by default retrieves the sharp hours that\nexist within the availability time slot. It uses the\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findBookingUnitBoundaries"}]},{"type":"text","value":" function, which is a recursive function\nthat checks whether the current boundary (e.g. sharp hour) passed to\nit falls within the availability time slot.\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"If the current boundary is within the availability time slot, the\nfunction calls itself with the next boundary and cumulates the\nboundary results into an array."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"If the current boundary does not fall within the availability time\nslot, the function returns the cumulated results from the previous\niterations."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findBookingUnitBoundaries"}]},{"type":"text","value":" takes a "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"nextBoundaryFn"}]},{"type":"text","value":" parameter that it\nuses to determine the next boundary value to pass to itself."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"text","value":"the function passed to "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findBookingUnitBoundaries"}]},{"type":"text","value":" as "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"nextBoundaryFn"}]},{"type":"text","value":"\nby default is "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"text","value":", which is what we need to modify\nfirst. The "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"text","value":" function increments the current boundary\nby a predefined value."}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"export"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function-variable","function"]},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"timeZone"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" currentMomentOrDate"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"moment"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"currentMomentOrDate"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"clone"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"tz"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"timeZone"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"add"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"1"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'hour'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"// The default handling uses hours"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"startOf"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'hour'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"// By default, the time slot is rounded to the start of the hour"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"toDate"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"add-a-custom-rounding-function-for-momentjs","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#add-a-custom-rounding-function-for-momentjs","ariaLabel":"add a custom rounding function for momentjs permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Add a custom rounding function for moment.js"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"FTW-hourly uses the "},{"type":"element","tagName":"a","properties":{"href":"https://momentjs.com/timezone/","target":"_blank","rel":["noopener","noreferrer"]},"children":[{"type":"text","value":"moment-timezone"}]},{"type":"text","value":"\nlibrary to modify times and dates and convert them between the listing's\ntime zone and the user's time zone."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"By default, the "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"text","value":" function uses\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"moment.startOf('hour')"}]},{"type":"text","value":" to round the booking slots to the top of each\nhour. However, since we are now dealing with minutes, we need to create\na custom rounding function to replace the "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"startOf('hour')"}]},{"type":"text","value":" function\ncall. When we add it to "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"moment.js"}]},{"type":"text","value":" using the prototype exposed through\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"moment.fn"}]},{"type":"text","value":", we can chain it in the same place as the default\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"startOf('hour')"}]},{"type":"text","value":" function."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"This rounding function rounds to sharp hours when the buffer minutes\nvalue is a factor of an hour, e.g. 15, 20 or 30 minutes."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"/**\n * Rounding function for moment.js. Rounds the Moment provided by the context\n * to the start of the specified time value in the specified units.\n * @param {*} value the rounding value\n * @param {*} timeUnit time units to specify the value\n * @returns Moment rounded to the start of the specified time value\n */"}]},{"type":"text","value":"\nmoment"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"fn"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function-variable","function"]},"children":[{"type":"text","value":"startOfDuration"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"function"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"value"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" timeUnit"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function-variable","function"]},"children":[{"type":"text","value":"getMs"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"val"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" unit"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" moment"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"duration"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"val"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" unit"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"_milliseconds"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" ms "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getMs"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"value"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" timeUnit"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n\n  "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"// Get UTC offset to account for potential time zone difference between"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"// customer and listing"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" offsetMs "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"this"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"_isUTC "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"?"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","number"]},"children":[{"type":"text","value":"0"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":":"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"getMs"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"this"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"utcOffset"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","string"]},"children":[{"type":"text","value":"'minute'"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"moment"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"Math"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"floor"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"this"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"valueOf"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" offsetMs"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"/"}]},{"type":"text","value":" ms"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"*"}]},{"type":"text","value":" ms"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"}"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"You will then need to use the new function to replace the built-in\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"startOf()"}]},{"type":"text","value":" function."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We also need to calculate the increment of time to add to each time\nboundary, i.e. how long are the stretches of time delineated by the\nboundaries. To do that, we need an "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"isStart"}]},{"type":"text","value":" attribute, i.e. whether\nwe're dealing with start times or end times."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In addition we need "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"isFirst"}]},{"type":"text","value":", i.e. whether the boundary in question is\nthe very first one in the list. Since we're rounding to the buffer time\n(here: 15 minutes), we'll need to manually set the first time slot to\ncorrespond to the start of the available time slot."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" export const findNextBoundary = (timeZone, currentMomentOrDate) =>\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"   moment(currentMomentOrDate)\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" export const findNextBoundary = (\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   timeZone,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   currentMomentOrDate,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   isFirst,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   isStart\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" ) => {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   const isEndTimeSlot = !isStart;\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   // For end time slots, add a full hour.\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   // For the first start slot, use the actual start time.\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   // For other start slots, use the buffer time.\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   const increment = isEndTimeSlot\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     ? hourMinutes\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     : isFirst\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     ? 0\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     : bufferMinutes;\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   return moment(currentMomentOrDate)\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     .clone()\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     .tz(timeZone)\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"     .add(1, 'hour')\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"     .startOf('hour')\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     .add(increment, 'minute')\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     .startOfDuration(bufferMinutes, 'minute')\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     .toDate();\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"  };"}]}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"extrainfo","properties":{"title":"Fetching time slots for the same day"},"children":[{"type":"text","value":"\nThe start of the time slot is in fact determined with the same "},{"type":"element","tagName":"i","properties":{},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"text","value":" function in "},{"type":"element","tagName":"i","properties":{},"children":[{"type":"text","value":"ListingPage.duck.js"}]},{"type":"text","value":".\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"shell"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-shell"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-shell"]},"children":[{"type":"text","value":"└── src\n    └── containers\n        └── ListingPage\n            └── ListingPage.duck.js"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"If we don't make any changes to this function call, the time slots for\nthe current day get fetched with the logic of the end hours, i.e. adding\none hour and rounding back to the previous 15 minutes. This would mean\nthat providers would have at least 45 minutes of warning in case someone\nbooks an appointment for the same day. If you want to fetch the time\nslot based on the start time cadence i.e. on the next 15 minutes, you\nwill need to pass the correct "},{"type":"element","tagName":"i","properties":{},"children":[{"type":"text","value":"isFirst"}]},{"type":"text","value":" and "},{"type":"element","tagName":"i","properties":{},"children":[{"type":"text","value":"isStart"}]},{"type":"text","value":"\nparameters."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"js"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-js"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","comment"]},"children":[{"type":"text","value":"// Helper function for loadData call."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function-variable","function"]},"children":[{"type":"text","value":"fetchMonthlyTimeSlots"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","parameter"]},"children":[{"type":"text","value":"dispatch"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" listing"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"=>"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"{"}]},{"type":"text","value":"\n  "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"..."}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" tz "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" listing"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"attributes"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"availabilityPlan"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"timezone"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"const"}]},{"type":"text","value":" nextBoundary "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"="}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"tz"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"new"}]},{"type":"text","value":" "},{"type":"element","tagName":"span","properties":{"className":["token","class-name"]},"children":[{"type":"text","value":"Date"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":";"}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"..."}]},{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["token","keyword"]},"children":[{"type":"text","value":"return"}]},{"type":"text","value":" Promise"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"all"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"["}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"dispatch"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"element","tagName":"span","properties":{"className":["token","function"]},"children":[{"type":"text","value":"fetchTimeSlots"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"("}]},{"type":"text","value":"listing"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":"."}]},{"type":"text","value":"id"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" nextBoundary"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" nextMonth"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":" tz"},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":")"}]},{"type":"element","tagName":"span","properties":{"className":["token","punctuation"]},"children":[{"type":"text","value":","}]},{"type":"text","value":"\n      "},{"type":"element","tagName":"span","properties":{"className":["token","operator"]},"children":[{"type":"text","value":"..."}]}]}]}]},{"type":"text","value":"\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"add-new-parameters-to-helper-functions","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#add-new-parameters-to-helper-functions","ariaLabel":"add new parameters to helper functions permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Add new parameters to helper functions"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"text","value":" function is called from the\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findBookingUnitBoundaries"}]},{"type":"text","value":" function, so we need to make sure the\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"isStart"}]},{"type":"text","value":" and "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"isFirst"}]},{"type":"text","value":" parameters are passed correctly. The function\ngets "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"text","value":" function as the "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"nextBoundaryFn"}]},{"type":"text","value":" parameter."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"text","value":"const findBookingUnitBoundaries = params => {\n"},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" const {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   cumulatedResults,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   currentBoundary,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   startMoment,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   endMoment,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   nextBoundaryFn,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   intl,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   timeZone,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   isStart,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" } = params;\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" if (moment(currentBoundary).isBetween(startMoment, endMoment, null, '[]')) {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" ...\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   // The nextBoundaryFn by definition cannot determine the first timepoint, since it\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   // is always based on a previous boundary, we pass 'false' as the 'isFirst' param\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   const isFirst = false;\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   return findBookingUnitBoundaries({\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     ...params,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     cumulatedResults: [...cumulatedResults, ...newBoundary],\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"     currentBoundary: moment(nextBoundaryFn(timeZone, currentBoundary)),\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     currentBoundary: moment(nextBoundaryFn(timeZone, currentBoundary, isFirst, isStart)),\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   });\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" }\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" return cumulatedResults;\n"}]},{"type":"text","value":"};"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"The "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findBookingUnitBoundaries"}]},{"type":"text","value":", in turn, is called from\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getSharpHours"}]},{"type":"text","value":". We need to pass the "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"isStart"}]},{"type":"text","value":" and "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"isFirst"}]},{"type":"text","value":" parameters\nwith the first currentBoundary definition in\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findBookingUnitBoundaries"}]},{"type":"text","value":", as well as add the "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"isStart"}]},{"type":"text","value":" parameter to\nthe "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findBookingUnitBoundaries"}]},{"type":"text","value":" function call."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In addition, we need to use the actual start time instead of the one\nmillisecond before, which is used by default. This is necessary because\ninstead of adding an hour and rounding off an hour as in the default\nimplementation, we are now manually setting the start time to the\nbeginning of the available time slot."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" export const getSharpHours = (intl, timeZone, startTime, endTime) => {\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" export const getSharpHours = (intl, timeZone, startTime, endTime, isStart = false) => {\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   if (!moment.tz.zone(timeZone)) {\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" ...\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"   const millisecondBeforeStartTime = new Date(startTime.getTime() - 1);\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   // For the first currentBoundary, we pass isFirst as true\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   const isFirst = true;\n"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   return findBookingUnitBoundaries({\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"     currentBoundary: findNextBoundary(timeZone, millisecondBeforeStartTime),\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     currentBoundary: findNextBoundary(timeZone, startTime, isFirst, isStart),\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     startMoment: moment(startTime),\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     endMoment: moment(endTime),\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     nextBoundaryFn: findNextBoundary,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     cumulatedResults: [],\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     intl,\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"     timeZone,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"     isStart,\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":"   });\n"}]},{"type":"text","value":"};\n"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h3","properties":{"id":"fix-getstarthours-and-getendhours-handling","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#fix-getstarthours-and-getendhours-handling","ariaLabel":"fix getstarthours and getendhours handling permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Fix getStartHours and getEndHours handling"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"To get correct start times, we need to first pass "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"true"}]},{"type":"text","value":" as the\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"isStart"}]},{"type":"text","value":" parameter from "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getStartHours"}]},{"type":"text","value":" to "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getSharpHours"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"In addition, we again need to make sure that even when selecting the\nlast start time, there is enough availability for the first timeslot.\nSince the first time slots are now set at the buffer minute interval, we\ndivide the full booking time by "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"bufferMinutes"}]},{"type":"text","value":" to get the correct\n"},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"removeCount"}]},{"type":"text","value":" value."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"text","value":"export const getStartHours = (intl, timeZone, startTime, endTime) => {\n"},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" const hours = getSharpHours(intl, timeZone, startTime, endTime);\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":" const removeCount = Math.ceil((hourMinutes + bufferMinutes) / hourMinutes)\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" const hours = getSharpHours(intl, timeZone, startTime, endTime, true);\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":" const removeCount = Math.ceil((hourMinutes + bufferMinutes) / bufferMinutes)\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" return hours.length < removeCount ? [] : hours.slice(0, -removeCount);\n"}]},{"type":"text","value":"};"}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Finally, we can simplify the end hour handling. Since the first entry is\ndetermined in the "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"findNextBoundary"}]},{"type":"text","value":" function, we do not need to remove\nit. Instead, we can just return the full list from "},{"type":"element","tagName":"em","properties":{},"children":[{"type":"text","value":"getSharpHours"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"div","properties":{"className":["gatsby-highlight"],"dataLanguage":"diff"},"children":[{"type":"element","tagName":"pre","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"code","properties":{"className":["language-diff"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" export const getEndHours = (intl, timeZone, startTime, endTime) => {\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","deleted-sign","deleted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"   const hours = getSharpHours(intl, timeZone, startTime, endTime);\n"},{"type":"element","tagName":"span","properties":{"className":["token","prefix","deleted"]},"children":[{"type":"text","value":"-"}]},{"type":"text","value":"   return hours.length < 2 ? [] : hours.slice(1);\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","inserted-sign","inserted"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","inserted"]},"children":[{"type":"text","value":"+"}]},{"type":"text","value":"   return getSharpHours(intl, timeZone, startTime, endTime);\n"}]},{"type":"element","tagName":"span","properties":{"className":["token","unchanged"]},"children":[{"type":"element","tagName":"span","properties":{"className":["token","prefix","unchanged"]},"children":[{"type":"text","value":" "}]},{"type":"text","value":" };"}]}]}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Now, even if we have a booking from 6 AM to 7 AM with a 15 minute buffer\nat the end, the next customer can start their booking at 7:15 AM.\nConversely, the previous booking can begin 4:45 AM and no later, so that\nthe buffered time slot can fit in before the already booked session."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"element","tagName":"span","properties":{"className":["gatsby-resp-image-wrapper"],"style":"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 300px; "},"children":[{"type":"text","value":"\n      "},{"type":"element","tagName":"a","properties":{"className":["gatsby-resp-image-link"],"href":"/docs/legacy/static/4081c6dcec2907432eab6e00cb77de14/5b7bc/quarter_hour_starts.png","style":"display: block","target":"_blank","rel":["noopener"]},"children":[{"type":"text","value":"\n    "},{"type":"element","tagName":"span","properties":{"className":["gatsby-resp-image-background-image"],"style":"padding-bottom: 189.937106918239%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAmCAYAAADEO7urAAAACXBIWXMAABYlAAAWJQFJUiTwAAAFDklEQVRIx41WS4/bVBidPwNIdNEdiDUbWHQF7YK/UNRKLIANG1BXbcWSDRI/gQpRIaoZqqHVTOs4cZzEsZ2ZJH47STPJxPHbcQ76rsczySQptfTpXl/7Hp/vda73sizDYrFAECc4DyJ4UQw/ipGmKZIkYSO9s8tobzmS7dFNvlhAHs9wbI1xZL6B4JxhNBpd2q4rz/M1sCvAPIdt27AsC8PhEK7rQtM09Pt9dt/r9di9ruswTROGYTALw3ADlAEul0s0m01wHAdRFNlGAiMj8G63yz5I9wROz2n0PG87IC3SS2qnA0VR2Avvcm11uQwqsSSjixJRGj1bHVfXtyblOmAJuiubu+YbLlM2KV6UBIpXub66+V3KZ6+8ISDHcS6zHEURgiBAEidYLvK32ipL5vJ0OoWum1DVDmRZgaKo6HROYVkOTNvGNJzBS3zMkjlmic/mq5Zm6RXDgt2Iba7XG+A4HhxXgSTJcJ0hunofljfAODm/tFE0WbMkSzYBTdNGsylBFJsMWFVPYNsuTMNCGiVIogS+5yP0Q+RZDiyWl7bhMgHa9gD1ugierzGWktSG4wxgGCbiOIY7cNHtddHXNARhgHy5ZK5mi2wTcDR6A0XpQBDqqFYF1Gp15jIB9vsaZrMZ4jDC2ZsxTM2AZZgwNB3Il4w5fXDNZSqZXk9j7hagNfYBAjw97WJ2PsPIn8CcDWBMHBhTF/rEYfEbBGeIkpgJzLWkuGi12mg0WowlZZliqOsGK6EkTxGkEcIsLmwRw09CtsbczrYkhZJRqVSZy7KsMoZUTvP5HFlayFxh+cqYb08KARK7Wk2AIIgXgEN0uz0mWefn50VXLP6nU1brUBQbLMvEsN2WL7JsIYrWdY/mZVuutucaQwIkZjxfvUiKykqJyoaEtGx+2kgtSWsUWxpJfbYyJGZlDCnLlJTrgHTOkLBSKZHRnNa2AhJDAiSWlPGysFelXpZlptyk4qTaqqoyxuXzjV4mdjSWWaYYlgxpA7lJhUwgdK4Q+E7AkmGlwm8wvC6kpfuleu9wuc76+HoMicHqEUBz+kh5zBLrnfJV9jK5XGTZuozhKjsCmUwmGI/Hm728CnhVh8pWl0v3aI0Uno6L1aRtuFyWTak2b3OZWtL3/e11WLYexZBYFoXtbmVIJyOd461Wi5URgW4wJMBWU2IiSwxLtSlaLypimC+YZRdxLNZ2tJ5BR0CrDUFsguNr6Jx0Ydou+pqB6WQKfz6HN5th7nmIKGakMlkhWwsmGgs2Z4DucIiOqeN1s45D/hVeCByqaguKo0HVe5h5HsI4wjwIEEQhwjhGkqWIs5SNa3VIumaPXNQsBQfNIzzln+Np9TkOlFdoDk7xm/wXbgkPcLv9GF9ID/Gl9BB32o9xR3qE29IjfNX+GXowAnIgLQGtkYNjq4VnyjGeCAd4Uv8Hf5+8AjeQ8av8FJ/yP+Czxk+41XiAzxs/4n3uLj7gvsZ7r+/iRuUe1Ll1AZheAXKWxFj9KR7id34fB90KqgMVv7T/wM3j+/io9h0+qX2Pj2vf4kblPrMPK/dwk/8GHd9eB3RGAzStDo6UGvaFl3hW/ReveyIazgle9uvYH9bxYtrG4URi9nIqX9rRVME8DdkvSVE2WcaScmLqEBQJRwKPozoP8USGauvomQYQ5wD9lOXbLc+uanSPitKybWiGBdMd4FTTIXU6MBwHfdNCV9dxNjnDPPDh+fOtRhil/Qce7BZRtkHB0gAAAABJRU5ErkJggg=='); background-size: cover; display: block;"},"children":[]},{"type":"text","value":"\n  "},{"type":"element","tagName":"picture","properties":{},"children":[{"type":"text","value":"\n          "},{"type":"element","tagName":"source","properties":{"srcSet":["/docs/legacy/static/4081c6dcec2907432eab6e00cb77de14/82e29/quarter_hour_starts.webp 159w","/docs/legacy/static/4081c6dcec2907432eab6e00cb77de14/02dbd/quarter_hour_starts.webp 300w"],"sizes":"(max-width: 300px) 100vw, 300px","type":"image/webp"},"children":[]},{"type":"text","value":"\n          "},{"type":"element","tagName":"source","properties":{"srcSet":["/docs/legacy/static/4081c6dcec2907432eab6e00cb77de14/8b9b5/quarter_hour_starts.png 159w","/docs/legacy/static/4081c6dcec2907432eab6e00cb77de14/5b7bc/quarter_hour_starts.png 300w"],"sizes":"(max-width: 300px) 100vw, 300px","type":"image/png"},"children":[]},{"type":"text","value":"\n          "},{"type":"element","tagName":"img","properties":{"className":["gatsby-resp-image-image"],"src":"/docs/legacy/static/4081c6dcec2907432eab6e00cb77de14/5b7bc/quarter_hour_starts.png","alt":"Booking start options buffered time slots","title":"Booking start options buffered time slots","loading":"lazy","decoding":"async","style":"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"},"children":[]},{"type":"text","value":"\n        "}]},{"type":"text","value":"\n  "}]},{"type":"text","value":"\n    "}]}]}],"data":{"quirksMode":false}},"headings":[{"value":"Add a 15 minute buffer between one hour bookings","depth":2},{"value":"Add display end time handling","depth":3},{"value":"Set getStartHours to return correct available start times","depth":3},{"value":"Allow users to book buffered appointments at non-sharp hours","depth":2},{"value":"Add a custom rounding function for moment.js","depth":3},{"value":"Add new parameters to helper functions","depth":3},{"value":"Fix getStartHours and getEndHours handling","depth":3}]}},"pageContext":{"slug":"bookings-with-buffer","category":"how-to-listing"}},
    "staticQueryHashes": ["3794076007","439097193","717698143"]}