From 3317c969e68c38bdb04590e69a6235a1452be469 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Thu, 31 Oct 2024 15:26:00 +0200 Subject: [PATCH 01/39] Rename files: AvailabilityModeSelector > AvailabilitySingleSeatSelector --- ...ilabilityModeSelector.js => AvailabilitySingleSeatSelector.js} | 0 ...ector.module.css => AvailabilitySingleSeatSelector.module.css} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/{AvailabilityModeSelector.js => AvailabilitySingleSeatSelector.js} (100%) rename src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/{AvailabilityModeSelector.module.css => AvailabilitySingleSeatSelector.module.css} (100%) diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilityModeSelector.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.js similarity index 100% rename from src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilityModeSelector.js rename to src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.js diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilityModeSelector.module.css b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.module.css similarity index 100% rename from src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilityModeSelector.module.css rename to src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.module.css From 377bdca837aac3b759af4f05bc24d4a529bda02b Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Thu, 31 Oct 2024 15:30:25 +0200 Subject: [PATCH 02/39] AvailabilitySingleSeatSelector: refactor --- .../AvailabilitySingleSeatSelector.js | 25 +++++++++++++++---- .../AvailabilitySingleSeatSelector.module.css | 2 +- .../EditListingAvailabilityExceptionForm.js | 11 +++++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.js index f65afe6f5..12adc2100 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.js @@ -1,12 +1,27 @@ import React from 'react'; +import classNames from 'classnames'; + import { FieldRadioButton } from '../../../../../components'; -import css from './AvailabilityModeSelector.module.css'; +import css from './AvailabilitySingleSeatSelector.module.css'; + +/** + * A Form Field that allows marking singleSeat availability (allow/block) + * + * @param {Object} props + * @param {string?} props.className + * @param {string?} props.rootClassName + * @param {string?} props.idPrefix + * @param {boolean?} props.pristine + * @param {ReactIntl} props.intl + * @returns {JSX.Element} Form Field looking like a radio button + */ +const AvailabilitySingleSeatSelector = props => { + const { rootClassName, className, idPrefix, pristine, intl } = props; + const classes = classNames(rootClassName || css.root, className); -const AvailabilityModeSelector = props => { - const { idPrefix, pristine, intl } = props; return ( -
+
{ ); }; -export default AvailabilityModeSelector; +export default AvailabilitySingleSeatSelector; diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.module.css b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.module.css index e6c80e9dd..4d51bc408 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.module.css +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/AvailabilitySingleSeatSelector.module.css @@ -1,6 +1,6 @@ @import '../../../../../styles/customMediaQueries.css'; -.radioButtons { +.root { } .checkedAvailable { diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js index 5e4b1eec3..26e2646e6 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js @@ -9,7 +9,7 @@ import { propTypes } from '../../../../../util/types'; import { Form, H3, PrimaryButton } from '../../../../../components'; -import AvailabilityModeSelector from './AvailabilityModeSelector'; +import AvailabilitySingleSeatSelector from './AvailabilitySingleSeatSelector'; import ExceptionDateTimeRange from './ExceptionDateTimeRange'; import ExceptionDateRange from './ExceptionDateRange'; @@ -85,9 +85,12 @@ const EditListingAvailabilityExceptionForm = props => { -
- -
+
{useFullDays ? ( From 67869088fad013ac049e860b7a46c0b7e4a4d609 Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Fri, 1 Nov 2024 10:24:56 +0200 Subject: [PATCH 03/39] AvailabilitySingleSeatSelector: use conditionally (when not dealing with multiple seats) --- .../EditListingAvailabilityExceptionForm.js | 18 +++++++++++------- .../EditListingAvailabilityPanel.js | 6 +++++- src/util/types.js | 4 ++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js index 26e2646e6..f7b449f32 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js @@ -5,7 +5,7 @@ import { Form as FinalForm } from 'react-final-form'; import classNames from 'classnames'; import { intlShape, injectIntl, FormattedMessage } from '../../../../../util/reactIntl'; -import { propTypes } from '../../../../../util/types'; +import { AVAILABILITY_MULTIPLE_SEATS, propTypes } from '../../../../../util/types'; import { Form, H3, PrimaryButton } from '../../../../../components'; @@ -39,6 +39,7 @@ const EditListingAvailabilityExceptionForm = props => { allExceptions, onFetchExceptions, useFullDays, + listingTypeConfig, isDaily, timeZone, updateInProgress, @@ -55,6 +56,7 @@ const EditListingAvailabilityExceptionForm = props => { exceptionEndTime, exceptionRange, } = values; + const hasMultipleSeatsInUSe = listingTypeConfig.availabilityType === AVAILABILITY_MULTIPLE_SEATS; const { updateListingError } = fetchErrors || {}; @@ -85,12 +87,14 @@ const EditListingAvailabilityExceptionForm = props => { - + {!hasMultipleSeatsInUSe ? ( + + ) : null}
{useFullDays ? ( diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js index 19c8928e8..4d70e7777 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js @@ -108,6 +108,7 @@ const EditListingAvailabilityPanel = props => { params, locationSearch, listing, + listingTypes, monthlyExceptionQueries, weeklyExceptionQueries, allExceptions, @@ -134,7 +135,9 @@ const EditListingAvailabilityPanel = props => { const firstDayOfWeek = config.localization.firstDayOfWeek; const classes = classNames(rootClassName || css.root, className); const listingAttributes = listing?.attributes; - const unitType = listingAttributes?.publicData?.unitType; + const { listingType, unitType } = listingAttributes?.publicData || {}; + const listingTypeConfig = listingTypes.find(conf => conf.listingType === listingType); + const useFullDays = isFullDay(unitType); const hasAvailabilityPlan = !!listingAttributes?.availabilityPlan; const isPublished = listing?.id && listingAttributes?.state !== LISTING_STATE_DRAFT; @@ -333,6 +336,7 @@ const EditListingAvailabilityPanel = props => { isDaily={unitType === DAY} updateInProgress={updateInProgress} useFullDays={useFullDays} + listingTypeConfig={listingTypeConfig} /> ) : null} diff --git a/src/util/types.js b/src/util/types.js index 1c4178b0a..6c0cddd80 100644 --- a/src/util/types.js +++ b/src/util/types.js @@ -338,6 +338,10 @@ export const STOCK_TYPES = [ STOCK_INFINITE_MULTIPLE_ITEMS, ]; +export const AVAILABILITY_ONE_SEAT = 'oneItem'; +export const AVAILABILITY_MULTIPLE_SEATS = 'multipleSeats'; +export const AVAILABILITY_TYPES = [AVAILABILITY_ONE_SEAT, AVAILABILITY_MULTIPLE_SEATS]; + propTypes.transition = shape({ createdAt: instanceOf(Date).isRequired, by: oneOf(TX_TRANSITION_ACTORS).isRequired, From bb2b0188aa020cdc8dc82e3330b12eb4e913bc0d Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 4 Nov 2024 18:36:32 +0200 Subject: [PATCH 04/39] Add FieldSeatsInput component --- .../FieldSeatsInput/FieldSeatsInput.js | 64 +++++++++++++++++++ .../FieldSeatsInput.module.css | 5 ++ src/translations/en.json | 5 ++ 3 files changed, 74 insertions(+) create mode 100644 src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/FieldSeatsInput/FieldSeatsInput.js create mode 100644 src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/FieldSeatsInput/FieldSeatsInput.module.css diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/FieldSeatsInput/FieldSeatsInput.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/FieldSeatsInput/FieldSeatsInput.js new file mode 100644 index 000000000..8ebe8d097 --- /dev/null +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/FieldSeatsInput/FieldSeatsInput.js @@ -0,0 +1,64 @@ +import React from 'react'; +import classNames from 'classnames'; + +import { validateInteger } from '../../../../../util/validators'; + +import { FieldTextInput } from '../../../../../components'; + +import css from './FieldSeatsInput.module.css'; + +const validate = (value, min, max, intl) => { + const requiredMsg = intl.formatMessage({ id: 'FieldSeatsInput.seatsError' }); + const numberTooSmallMessage = intl.formatMessage( + { id: 'FieldSeatsInput.numberTooSmall' }, + { min } + ); + const numberTooBigMessage = intl.formatMessage({ id: 'FieldSeatsInput.numberTooBig' }, { max }); + + return value == null + ? requiredMsg + : validateInteger(value, max, min, numberTooSmallMessage, numberTooBigMessage); +}; + +/** + * React Final Form Field to set the number of seats. + * + * @component + * @param {Object} props + * @param {String?} props.rootClassName - a class name that overwrites 'root' class + * @param {String?} props.className - a class name to add next to 'root' class + * @param {String?} props.inputRootClass - a class name to pass to input element + * @param {String} props.id - the id of the element + * @param {String} props.name - the name of the Final Form Field (input). + * @param {String} props.unitType - 'hour', 'day', 'night' + * @param {ReactIntl} props.intl - instance of React Intl + * @returns {JSX.Element} seats input for the React Final Form. + */ +const FieldSeatsInput = props => { + const { rootClassName, className, inputRootClass, id, name, unitType, intl } = props; + const classes = classNames(rootClassName || css.root, className); + + return ( +
+ { + const parsed = Number.parseInt(value, 10); + return Number.isNaN(parsed) ? null : parsed; + }} + label={intl.formatMessage({ id: 'FieldSeatsInput.seatsLabel' }, { unitType })} + placeholder={intl.formatMessage({ id: 'FieldSeatsInput.seatsPlaceholder' }, { unitType })} + validate={value => validate(value, 0, Number.MAX_SAFE_INTEGER, intl)} + /> +
+ ); +}; + +export default FieldSeatsInput; diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/FieldSeatsInput/FieldSeatsInput.module.css b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/FieldSeatsInput/FieldSeatsInput.module.css new file mode 100644 index 000000000..ea12bc4b8 --- /dev/null +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/FieldSeatsInput/FieldSeatsInput.module.css @@ -0,0 +1,5 @@ +@import '../../../../../styles/customMediaQueries.css'; + +.root { + margin-top: 24px; +} diff --git a/src/translations/en.json b/src/translations/en.json index 17e054c38..1d1d4bac5 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -365,6 +365,11 @@ "FieldReviewRating.star3": "OK - 3 stars", "FieldReviewRating.star4": "Good - 4 stars", "FieldReviewRating.star5": "Awesome - 5 stars", + "FieldSeatsInput.numberTooBig": "The number must be less than or equal to {max}", + "FieldSeatsInput.numberTooSmall": "The number must be greater than or equal to {min}", + "FieldSeatsInput.seatsError": "This is required", + "FieldSeatsInput.seatsLabel": "Seats", + "FieldSeatsInput.seatsPlaceholder": "Seats {unitType, select, hour {per hour} day {per day} night {per night} other {per unit}}", "FieldSelectUserType.label": "User type", "FieldSelectUserType.placeholder": "Choose a user type", "FieldSelectUserType.required": "You need to select a user type.", From 2dfe548498ed9fe4ea26cf872fa009f0cbf4b21f Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 4 Nov 2024 18:42:16 +0200 Subject: [PATCH 05/39] EditListingAvailabilityExceptionForm: take AvailabilityMultipleSeatsInput into use --- ...istingAvailabilityExceptionForm.example.js | 3 +- .../EditListingAvailabilityExceptionForm.js | 33 +++++++++++++++---- ...istingAvailabilityExceptionForm.module.css | 9 +++-- .../EditListingAvailabilityPanel.js | 2 +- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.example.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.example.js index c8755fc15..bffa551ee 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.example.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.example.js @@ -19,7 +19,8 @@ export const Example = { onFetchExceptions: values => console.log('onFetchExceptions', values), onMonthChanged: values => console.log('onMonthChanged', values), fetchErrors: {}, - isDaily: false, + listingTypeConfig: { availabilityType: 'oneSeat' }, + unitType: 'hour', useFullDays: false, intl: fakeIntl, timeZone: 'Etc/UTC', diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js index f7b449f32..5767126dd 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.js @@ -1,14 +1,16 @@ import React from 'react'; -import { array, arrayOf, bool, func, object, shape, string } from 'prop-types'; +import { array, arrayOf, bool, func, object, oneOf, shape, string } from 'prop-types'; import { compose } from 'redux'; import { Form as FinalForm } from 'react-final-form'; import classNames from 'classnames'; import { intlShape, injectIntl, FormattedMessage } from '../../../../../util/reactIntl'; import { AVAILABILITY_MULTIPLE_SEATS, propTypes } from '../../../../../util/types'; +import { DAY } from '../../../../../transactions/transaction'; import { Form, H3, PrimaryButton } from '../../../../../components'; +import FieldSeatsInput from '../FieldSeatsInput/FieldSeatsInput'; import AvailabilitySingleSeatSelector from './AvailabilitySingleSeatSelector'; import ExceptionDateTimeRange from './ExceptionDateTimeRange'; import ExceptionDateRange from './ExceptionDateRange'; @@ -40,7 +42,7 @@ const EditListingAvailabilityExceptionForm = props => { onFetchExceptions, useFullDays, listingTypeConfig, - isDaily, + unitType, timeZone, updateInProgress, fetchErrors, @@ -48,6 +50,8 @@ const EditListingAvailabilityExceptionForm = props => { } = formRenderProps; const idPrefix = `${formId}` || 'EditListingAvailabilityExceptionForm'; + const isDaily = unitType === DAY; + const { availability, exceptionStartDate, @@ -55,14 +59,17 @@ const EditListingAvailabilityExceptionForm = props => { exceptionEndDate, exceptionEndTime, exceptionRange, + seats, } = values; - const hasMultipleSeatsInUSe = listingTypeConfig.availabilityType === AVAILABILITY_MULTIPLE_SEATS; + const hasMultipleSeatsInUSe = + listingTypeConfig.availabilityType === AVAILABILITY_MULTIPLE_SEATS; + const hasSeats = hasMultipleSeatsInUSe ? seats != null : availability; const { updateListingError } = fetchErrors || {}; const submitInProgress = updateInProgress; const hasData = - availability && + hasSeats && (exceptionRange || (exceptionStartDate && exceptionStartTime && exceptionEndDate && exceptionEndTime)); const submitDisabled = !hasData || invalid || disabled || submitInProgress; @@ -88,7 +95,7 @@ const EditListingAvailabilityExceptionForm = props => { {!hasMultipleSeatsInUSe ? ( - { )}
+ {hasMultipleSeatsInUSe ? ( + + ) : null} +
{updateListingError ? (

@@ -161,7 +179,10 @@ EditListingAvailabilityExceptionForm.propTypes = { allExceptions: arrayOf(propTypes.availabilityException), intl: intlShape.isRequired, onSubmit: func.isRequired, - isDaily: bool.isRequired, + listingTypeConfig: shape({ + availabilityType: oneOf(['oneSeat', 'multipleSeats']).isRequired, + }).isRequired, + unitType: string.isRequired, useFullDays: bool.isRequired, timeZone: string.isRequired, updateInProgress: bool.isRequired, diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.module.css b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.module.css index 89274ae5a..db4241fab 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.module.css +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityExceptionForm/EditListingAvailabilityExceptionForm.module.css @@ -5,6 +5,7 @@ .heading, .radioButtons, +.seatsInput, .submitButton { padding-left: 24px; padding-right: 24px; @@ -23,10 +24,8 @@ .section { display: flex; flex-direction: column; - margin-bottom: 48px; @media (--viewportMedium) { - margin-bottom: 200px; padding-left: 60px; padding-right: 60px; } @@ -38,13 +37,19 @@ fill: var(--colorWhite); } +.seatsInput { + margin-top: 24px; +} + .submitButton { margin-top: auto; flex-shrink: 0; /* Mobile phones introduce bottom-bar, for which we need to give 96px vertical space */ padding-bottom: 96px; + margin-top: 48px; @media (--viewportMedium) { + margin-top: 200px; padding-bottom: 0; } } diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js index 4d70e7777..3864befd7 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js @@ -333,7 +333,7 @@ const EditListingAvailabilityPanel = props => { onFetchExceptions={onFetchExceptions} onSubmit={saveException} timeZone={availabilityPlan.timezone} - isDaily={unitType === DAY} + unitType={unitType} updateInProgress={updateInProgress} useFullDays={useFullDays} listingTypeConfig={listingTypeConfig} From ed2eef3b4e436226087e7e602db13fa3e22b90cd Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 11 Nov 2024 15:45:43 +0200 Subject: [PATCH 06/39] EditListingAvailabilityPanel: rename some plan-related variables - initialValues > initialPlanValues - handleSubmit > handlePlanSubmit) --- .../EditListingAvailabilityPanel.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js index 3864befd7..c7c72a319 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPanel.js @@ -59,8 +59,8 @@ const createEntryDayGroups = (entries = {}) => { }, {}); }; -// Create initial values -const createInitialValues = availabilityPlan => { +// Create initial values for the availability plan +const createInitialPlanValues = availabilityPlan => { const { timezone, entries } = availabilityPlan || {}; const tz = timezone || defaultTimeZone(); return { @@ -155,11 +155,11 @@ const EditListingAvailabilityPanel = props => { ], }; const availabilityPlan = listingAttributes?.availabilityPlan || defaultAvailabilityPlan; - const initialValues = valuesFromLastSubmit + const initialPlanValues = valuesFromLastSubmit ? valuesFromLastSubmit - : createInitialValues(availabilityPlan); + : createInitialPlanValues(availabilityPlan); - const handleSubmit = values => { + const handlePlanSubmit = values => { setValuesFromLastSubmit(values); // Final Form can wait for Promises to return. @@ -307,8 +307,8 @@ const EditListingAvailabilityPanel = props => { availabilityPlan={availabilityPlan} weekdays={rotateDays(WEEKDAYS, firstDayOfWeek)} useFullDays={useFullDays} - onSubmit={handleSubmit} - initialValues={initialValues} + onSubmit={handlePlanSubmit} + initialValues={initialPlanValues} inProgress={updateInProgress} fetchErrors={errors} /> From 0eb481320b14570ccf419ed29388384cb9b56aeb Mon Sep 17 00:00:00 2001 From: Vesa Luusua Date: Mon, 11 Nov 2024 16:56:07 +0200 Subject: [PATCH 07/39] AvailabilityPlanEntries: refactor & add seats input if 'useMultipleSeats' flag is set --- .../AvailabilityPlanEntries.js | 353 ++++++++++++------ .../AvailabilityPlanEntries.module.css | 151 +++++--- src/translations/en.json | 1 + 3 files changed, 341 insertions(+), 164 deletions(-) diff --git a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPlanForm/AvailabilityPlanEntries.js b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPlanForm/AvailabilityPlanEntries.js index 77739cda4..f1c308fd2 100644 --- a/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPlanForm/AvailabilityPlanEntries.js +++ b/src/containers/EditListingPage/EditListingWizard/EditListingAvailabilityPanel/EditListingAvailabilityPlanForm/AvailabilityPlanEntries.js @@ -4,7 +4,15 @@ import { FieldArray } from 'react-final-form-arrays'; import classNames from 'classnames'; import { FormattedMessage } from '../../../../../util/reactIntl'; -import { InlineTextButton, IconClose, FieldSelect, FieldCheckbox } from '../../../../../components'; + +import { + InlineTextButton, + FieldSelect, + FieldCheckbox, + IconDelete, +} from '../../../../../components'; + +import FieldSeatsInput from '../FieldSeatsInput/FieldSeatsInput'; import css from './AvailabilityPlanEntries.module.css'; @@ -58,6 +66,16 @@ const sortEntries = (defaultCompareReturn = 0) => (a, b) => { // Curried: find entry by comparing start time and end time const findEntryFn = entry => e => e.startTime === entry.startTime && e.endTime === entry.endTime; +/** + * AvailabilityPlan entry. + * + * @typedef {Object} AvailabilityPlanEntry + * @property {String} dayOfWeek - the day of week shorthand. E.g. 'Mon'. + * @property {String} startTime - start hour. E.g. '09:00'. + * @property {String} endTime - end hour. E.g. '17:00'. + * @property {Number} seats - the number of available seats 0...Number.MAX_SAFE_INTEGER + */ + /** * From all the available start hours, filter only those start hours that can be used * in the current entry creation. @@ -65,8 +83,8 @@ const findEntryFn = entry => e => e.startTime === entry.startTime && e.endTime = * For start hours this mainly means situation where end hours is set first. * * @param {Array} availableStartHours (hours are in format: '13:00') - * @param {*} entries created entries: [{ startTime: '13:00', endTime: '17:00' }] - * @param {*} index index in the Final Form Array: current dayOfWeek + * @param {Array} entries created entries: [{ startTime: '13:00', endTime: '17:00' }] + * @param {Number} index index in the Final Form Array: current dayOfWeek * @returns returns only those start hours that are allowed to be selected. */ const filterStartHours = (availableStartHours, entries, index) => { @@ -103,8 +121,8 @@ const filterStartHours = (availableStartHours, entries, index) => { * For end hour this only means a situation where start hour is set first. * * @param {Array} availableEndHours (hours are in format: '13:00') - * @param {*} entries created entries: [{ startTime: '13:00', endTime: '17:00' }] - * @param {*} index index in the Final Form Array: current dayOfWeek + * @param {Array} entries created entries: [{ startTime: '13:00', endTime: '17:00' }] + * @param {Number} index index in the Final Form Array: current dayOfWeek * @returns returns only those end hours that are allowed to be selected. */ const filterEndHours = (availableEndHours, entries, index) => { @@ -138,9 +156,9 @@ const filterEndHours = (availableEndHours, entries, index) => { /** * Find all the entries that boundaries are already reserved. * - * @param {*} entries look like this [{ startTime: '13:00', endTime: '17:00' }] - * @param {*} intl - * @param {*} findStartHours find start hours (00:00 ... 23:00) or else (01:00 ... 24:00) + * @param {Array} entries look like this [{ startTime: '13:00', endTime: '17:00' }] + * @param {ReactIntl} intl + * @param {Boolean} findStartHours find start hours (00:00 ... 23:00) or else (01:00 ... 24:00) * @returns array of reserved sharp hours. E.g. ['13:00', '14:00', '15:00', '16:00'] */ const getEntryBoundaries = (entries, intl, findStartHours) => index => { @@ -165,6 +183,21 @@ const getEntryBoundaries = (entries, intl, findStartHours) => index => { /** * Date pickers that create time range inside the day: start hour - end hour + * + * @component + * @param {Object} props - The component props + * @param {string} props.name - the name of the form field/input + * @param {Number} props.index - the index in the Final Form Array for the current dayOfWeek + * @param {Array} props.availableStartHours - array of strings represeting start hours: '00:00', '01:00', etc. + * @param {Array} props.availableEndHours - array of strings represeting end hours: '01:00', '02:00', etc. + * @param {Function} props.isTimeSetFn - Check if 'startTime' or 'endTime' is set for the form + * @param {Boolean} props.isNextDay - flag if the selected 'endTime' is the next day aka (24:00) + * @param {Array} props.entries - AvailabilityPlan entries: [['Mon[0]']: ]] + * @param {Function} props.onRemove - a function to remove plan entry + * @param {String} props.unitType - 'hour', 'day', 'night' + * @param {Boolean} props.useMultipleSeats - true if availabilityType is 'multipleSeats' + * @param {ReactIntl} props.intl - React Intl instance + * @returns {JSX.Element} The component that allows selecting plan entries */ const TimeRangeSelects = props => { const { @@ -176,74 +209,116 @@ const TimeRangeSelects = props => { isNextDay, entries, onRemove, + unitType, + useMultipleSeats, intl, } = props; return ( -

-
- -