willba-component-library 0.2.63 → 0.2.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/FilterCalendar/FilterCalendar.d.ts +1 -1
- package/lib/components/FilterCalendar/hooks/useFilterCalendar.d.ts +4 -2
- package/lib/core/components/calendar/hooks/useUpdateDisabledDates.d.ts +2 -1
- package/lib/core/components/calendar/utils/handleCalendarModifiers.d.ts +2 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.esm.js +99 -32
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +99 -32
- package/lib/index.js.map +1 -1
- package/lib/index.umd.js +99 -32
- package/lib/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/FilterCalendar/FilterCalendar.css +12 -1
- package/src/components/FilterCalendar/FilterCalendar.tsx +44 -17
- package/src/components/FilterCalendar/hooks/useFilterCalendar.ts +19 -1
- package/src/core/components/calendar/Calendar.tsx +13 -15
- package/src/core/components/calendar/hooks/useUpdateDisabledDates.tsx +19 -2
- package/src/core/components/calendar/utils/handleCalendarModifiers.tsx +4 -2
- package/src/locales/en/common.json +9 -1
- package/src/locales/en/filterBar.json +2 -1
- package/src/locales/fi/common.json +9 -1
- package/src/locales/fi/filterBar.json +2 -1
package/package.json
CHANGED
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
.will-root .will-calendar-wrapper .will-calendar-footer-dates .will-calendar-footer-booked {
|
|
49
49
|
display: flex;
|
|
50
50
|
min-height: 20.5px;
|
|
51
|
+
margin-top: 10px;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
.will-root .will-calendar-wrapper .will-calendar-footer-actions {
|
|
@@ -56,6 +57,11 @@
|
|
|
56
57
|
gap: 10px;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
.will-root .will-calendar-wrapper .will-calendar-footer-error {
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
}
|
|
64
|
+
|
|
59
65
|
@media (max-width: 960px) {
|
|
60
66
|
.will-root .will-calendar-wrapper {
|
|
61
67
|
width: -webkit-fill-available;
|
|
@@ -67,7 +73,7 @@
|
|
|
67
73
|
padding: 20px 10px;
|
|
68
74
|
}
|
|
69
75
|
|
|
70
|
-
.will-root .will-calendar-wrapper .will-calendar-footer {
|
|
76
|
+
.will-root .will-calendar-wrapper .will-calendar-footer-actions-wrapper {
|
|
71
77
|
flex-direction: column;
|
|
72
78
|
}
|
|
73
79
|
|
|
@@ -88,6 +94,11 @@
|
|
|
88
94
|
.will-root .will-calendar-wrapper .will-calendar-footer-actions button{
|
|
89
95
|
width: 100%;
|
|
90
96
|
}
|
|
97
|
+
|
|
98
|
+
.will-root .will-calendar-wrapper .will-calendar-footer-error {
|
|
99
|
+
min-width: 20px;
|
|
100
|
+
margin-bottom: 10px;
|
|
101
|
+
}
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
.will-root .will-calendar-wrapper .will-calendar-header .will-filter-bar-close-button {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { useTranslation } from 'react-i18next'
|
|
3
|
+
import { useMediaQuery } from 'react-responsive'
|
|
3
4
|
|
|
4
|
-
import useTheme from '../../themes/useTheme'
|
|
5
|
+
import useTheme, { Palette } from '../../themes/useTheme'
|
|
5
6
|
import '../../themes/Default.css'
|
|
6
7
|
import { Calendar, SubmitButton, CloseButton } from '../../core/components'
|
|
7
8
|
import {
|
|
@@ -22,7 +23,7 @@ export default function FilterCalendar({
|
|
|
22
23
|
language,
|
|
23
24
|
palette,
|
|
24
25
|
onSubmit,
|
|
25
|
-
disableCalendarDates,
|
|
26
|
+
disableCalendarDates: outerDisableCalendarDates,
|
|
26
27
|
toggleCalendar,
|
|
27
28
|
loadingData,
|
|
28
29
|
setToggleCalendar,
|
|
@@ -36,7 +37,8 @@ export default function FilterCalendar({
|
|
|
36
37
|
// Translations
|
|
37
38
|
useUpdateTranslations({ language })
|
|
38
39
|
const { t } = useTranslation()
|
|
39
|
-
|
|
40
|
+
const isMobile = useMediaQuery({ maxWidth: 690 })
|
|
41
|
+
console.log('1', { outerDisableCalendarDates })
|
|
40
42
|
const {
|
|
41
43
|
setCalendarRange,
|
|
42
44
|
handleClearDates,
|
|
@@ -50,14 +52,18 @@ export default function FilterCalendar({
|
|
|
50
52
|
setCalendarHasError,
|
|
51
53
|
setUpdatedForSubmit,
|
|
52
54
|
rangeContext,
|
|
55
|
+
disableCalendarDates,
|
|
53
56
|
} = useFilterCalendar({
|
|
54
57
|
onSubmit,
|
|
55
58
|
setToggleCalendar,
|
|
56
59
|
noActiveSelection,
|
|
57
60
|
toggleCalendar,
|
|
58
61
|
outerRangeContext,
|
|
62
|
+
outerDisableCalendarDates,
|
|
59
63
|
})
|
|
60
64
|
|
|
65
|
+
console.log('1', { disableCalendarDates })
|
|
66
|
+
|
|
61
67
|
// Display component after fully loaded
|
|
62
68
|
useAwaitRender()
|
|
63
69
|
|
|
@@ -109,7 +115,7 @@ export default function FilterCalendar({
|
|
|
109
115
|
<div className="will-calendar-footer-dates">
|
|
110
116
|
{calendarHasError ? (
|
|
111
117
|
<span>
|
|
112
|
-
|
|
118
|
+
{t(`common:errors.calendarErrors.checkInAvailabilityGuide`)}
|
|
113
119
|
</span>
|
|
114
120
|
) : (
|
|
115
121
|
<div>
|
|
@@ -135,7 +141,7 @@ export default function FilterCalendar({
|
|
|
135
141
|
|
|
136
142
|
<span className="will-calendar-footer-booked">
|
|
137
143
|
{calendarHasError
|
|
138
|
-
?
|
|
144
|
+
? t(`filterBar:calendar.minNights`)
|
|
139
145
|
: nights
|
|
140
146
|
? `${nights} ${t(
|
|
141
147
|
`common:${nights === 1 ? 'night' : 'nights'}`
|
|
@@ -144,6 +150,15 @@ export default function FilterCalendar({
|
|
|
144
150
|
</span>
|
|
145
151
|
</div>
|
|
146
152
|
|
|
153
|
+
{calendarHasError &&
|
|
154
|
+
isMobile &&
|
|
155
|
+
renderCalendarErrorMessage({
|
|
156
|
+
palette,
|
|
157
|
+
message: t(
|
|
158
|
+
`common:errors.calendarErrors.checkInAvailabilityError`
|
|
159
|
+
),
|
|
160
|
+
})}
|
|
161
|
+
|
|
147
162
|
<div className="will-calendar-footer-actions">
|
|
148
163
|
<SubmitButton
|
|
149
164
|
onClick={handleClearDates}
|
|
@@ -152,21 +167,33 @@ export default function FilterCalendar({
|
|
|
152
167
|
/>
|
|
153
168
|
</div>
|
|
154
169
|
</div>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
</span>
|
|
165
|
-
</div>
|
|
166
|
-
)}
|
|
170
|
+
|
|
171
|
+
{calendarHasError &&
|
|
172
|
+
!isMobile &&
|
|
173
|
+
renderCalendarErrorMessage({
|
|
174
|
+
palette,
|
|
175
|
+
message: t(
|
|
176
|
+
`common:errors.calendarErrors.checkInAvailabilityError`
|
|
177
|
+
),
|
|
178
|
+
})}
|
|
167
179
|
</div>
|
|
168
180
|
</div>
|
|
169
181
|
)}
|
|
170
182
|
</div>
|
|
171
183
|
)
|
|
172
184
|
}
|
|
185
|
+
|
|
186
|
+
/////////
|
|
187
|
+
|
|
188
|
+
const renderCalendarErrorMessage = ({
|
|
189
|
+
message,
|
|
190
|
+
palette,
|
|
191
|
+
}: {
|
|
192
|
+
message?: string
|
|
193
|
+
palette: Palette
|
|
194
|
+
}) => (
|
|
195
|
+
<div className="will-calendar-footer-error">
|
|
196
|
+
<IconsSvg fill={palette?.error || 'inherit'} size={25} icon="warning" />
|
|
197
|
+
<span style={{ marginLeft: '10px' }}>{message || ''}</span>
|
|
198
|
+
</div>
|
|
199
|
+
)
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
import { DateRange, Matcher } from 'react-day-picker'
|
|
3
3
|
import { format } from 'date-fns'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DisableCalendarDates,
|
|
6
|
+
RangeContext,
|
|
7
|
+
} from 'src/core/components/calendar/CalendarTypes'
|
|
5
8
|
|
|
6
9
|
type Props = {
|
|
7
10
|
onSubmit: (val: any) => void
|
|
@@ -9,6 +12,7 @@ type Props = {
|
|
|
9
12
|
noActiveSelection?: boolean
|
|
10
13
|
toggleCalendar?: boolean
|
|
11
14
|
outerRangeContext?: RangeContext
|
|
15
|
+
outerDisableCalendarDates?: DisableCalendarDates
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
export const useFilterCalendar = ({
|
|
@@ -17,6 +21,7 @@ export const useFilterCalendar = ({
|
|
|
17
21
|
noActiveSelection,
|
|
18
22
|
toggleCalendar,
|
|
19
23
|
outerRangeContext,
|
|
24
|
+
outerDisableCalendarDates,
|
|
20
25
|
}: Props) => {
|
|
21
26
|
// State
|
|
22
27
|
const [calendarRange, setCalendarRange] = useState<DateRange | undefined>()
|
|
@@ -35,8 +40,19 @@ export const useFilterCalendar = ({
|
|
|
35
40
|
const [calendarHasError, setCalendarHasError] = useState<boolean>(false)
|
|
36
41
|
|
|
37
42
|
const [updatedForSubmit, setUpdatedForSubmit] = useState<boolean>(false)
|
|
43
|
+
const [disableCalendarDates, setDisableCalendarDates] =
|
|
44
|
+
useState<DisableCalendarDates>()
|
|
38
45
|
|
|
39
46
|
// Lifecycle
|
|
47
|
+
|
|
48
|
+
// Handle update component with new data
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (outerDisableCalendarDates) {
|
|
51
|
+
setDisableCalendarDates(outerDisableCalendarDates)
|
|
52
|
+
}
|
|
53
|
+
}, [outerDisableCalendarDates])
|
|
54
|
+
|
|
55
|
+
// Handle Range Context initial selections
|
|
40
56
|
useEffect(() => {
|
|
41
57
|
if (typeof window === 'undefined') return
|
|
42
58
|
|
|
@@ -68,6 +84,7 @@ export const useFilterCalendar = ({
|
|
|
68
84
|
}
|
|
69
85
|
}, [toggleCalendar])
|
|
70
86
|
|
|
87
|
+
// Handle submit dates
|
|
71
88
|
useEffect(() => {
|
|
72
89
|
const formatString = 'dd.MM.yyyy'
|
|
73
90
|
const initialRangeTo = initialCalendarRange?.to
|
|
@@ -122,5 +139,6 @@ export const useFilterCalendar = ({
|
|
|
122
139
|
setCalendarHasError,
|
|
123
140
|
setUpdatedForSubmit,
|
|
124
141
|
rangeContext,
|
|
142
|
+
disableCalendarDates,
|
|
125
143
|
}
|
|
126
144
|
}
|
|
@@ -55,15 +55,18 @@ export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
|
|
|
55
55
|
const selectedStartDate = calendarRange?.from
|
|
56
56
|
const rangeContextStartDate = rangeContext?.from
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
console.log({ disableCalendarDates })
|
|
59
|
+
|
|
60
|
+
// Handle initial disable dates including overlapping availableDates.lastCheckOut and disabledDates.start
|
|
61
|
+
const { newDisableCalendarDates, overlappingDate, lastPossibleCheckout } =
|
|
62
|
+
useUpdateDisabledDates({
|
|
61
63
|
disableCalendarDates,
|
|
62
64
|
calendarRange,
|
|
63
65
|
updateCalendarMonthNavigation,
|
|
64
66
|
updateCalendarDefaultMonth,
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
console.log({ newDisableCalendarDates })
|
|
67
70
|
|
|
68
71
|
// Handle disable dates by page
|
|
69
72
|
const disabledDatesByPage = handleDisabledDatesByPage({
|
|
@@ -100,6 +103,8 @@ export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
|
|
|
100
103
|
setUpdatedForSubmit && setUpdatedForSubmit(true)
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
console.log({ disabledDates })
|
|
107
|
+
|
|
103
108
|
// Handle disabled dates for range context
|
|
104
109
|
const {
|
|
105
110
|
findFirstPossibleRangeContextCheckIn,
|
|
@@ -121,14 +126,6 @@ export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
|
|
|
121
126
|
calendarHasError,
|
|
122
127
|
})
|
|
123
128
|
|
|
124
|
-
// Find last possible checkout for dates
|
|
125
|
-
const lastPossibleCheckout = !!newDisableCalendarDates?.availableDates
|
|
126
|
-
?.length
|
|
127
|
-
? newDisableCalendarDates?.availableDates[
|
|
128
|
-
newDisableCalendarDates.availableDates.length - 1
|
|
129
|
-
].lastCheckOut
|
|
130
|
-
: null
|
|
131
|
-
|
|
132
129
|
return (
|
|
133
130
|
<div className="will-filter-bar-calendar" ref={ref}>
|
|
134
131
|
<div className="will-calendar-filter-container">
|
|
@@ -155,14 +152,14 @@ export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
|
|
|
155
152
|
? disabledDatesByPage
|
|
156
153
|
: disabledDates?.length
|
|
157
154
|
? [
|
|
158
|
-
lastPossibleCheckout
|
|
155
|
+
lastPossibleCheckout && lastPossibleCheckout,
|
|
159
156
|
...disabledDates,
|
|
160
157
|
...firstPossibleRangeContextCheckIn,
|
|
161
158
|
...lastPossibleRangeContextCheckOut,
|
|
162
159
|
]
|
|
163
160
|
: newDisableCalendarDates?.disabledDates?.length
|
|
164
161
|
? [
|
|
165
|
-
lastPossibleCheckout
|
|
162
|
+
lastPossibleCheckout && lastPossibleCheckout,
|
|
166
163
|
...newDisableCalendarDates.disabledDates,
|
|
167
164
|
...firstPossibleRangeContextCheckIn,
|
|
168
165
|
...lastPossibleRangeContextCheckOut,
|
|
@@ -206,6 +203,7 @@ export const Calendar = forwardRef<HTMLDivElement, CalendarTypes>(
|
|
|
206
203
|
disabledDates,
|
|
207
204
|
overlappingDate,
|
|
208
205
|
rangeContext,
|
|
206
|
+
lastPossibleCheckout,
|
|
209
207
|
findFirstPossibleRangeContextCheckIn,
|
|
210
208
|
findLastPossibleRangeContextCheckOut,
|
|
211
209
|
firstPossibleRangeContextCheckIn,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMemo, useState } from 'react'
|
|
2
2
|
import { addDays, format } from 'date-fns'
|
|
3
|
-
import { DateRange } from 'react-day-picker'
|
|
3
|
+
import { DateRange, Matcher } from 'react-day-picker'
|
|
4
4
|
|
|
5
5
|
import { DisableCalendarDates } from '../CalendarTypes'
|
|
6
6
|
|
|
@@ -21,6 +21,8 @@ export const useUpdateDisabledDates = ({
|
|
|
21
21
|
DateRange[] | undefined
|
|
22
22
|
>(undefined)
|
|
23
23
|
|
|
24
|
+
const [lastPossibleCheckout, setLatsPossibleCheckout] = useState<Matcher>([])
|
|
25
|
+
|
|
24
26
|
const newDisableCalendarDates = useMemo(() => {
|
|
25
27
|
if (disableCalendarDates?.availableDates) {
|
|
26
28
|
const dateFormat = 'dd-MM-yyyy'
|
|
@@ -71,10 +73,25 @@ export const useUpdateDisabledDates = ({
|
|
|
71
73
|
{ updatedDisabledDates: [], newOverlappingDates: [] }
|
|
72
74
|
)
|
|
73
75
|
|
|
76
|
+
// Find last possible checkout ( disable all dates after the last possible checkout )
|
|
77
|
+
const lastPossibleCheckout =
|
|
78
|
+
disableCalendarDates.availableDates[
|
|
79
|
+
disableCalendarDates.availableDates.length - 1
|
|
80
|
+
].lastCheckOut
|
|
81
|
+
|
|
82
|
+
if (lastPossibleCheckout) {
|
|
83
|
+
setLatsPossibleCheckout({ after: lastPossibleCheckout })
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Extract overlapping dates ( dates that are only available for checkout )
|
|
74
87
|
if (newOverlappingDates.length) {
|
|
75
88
|
setOverlappingDate((prev = []) => [...prev, ...newOverlappingDates])
|
|
76
89
|
}
|
|
77
90
|
|
|
91
|
+
if (newOverlappingDates.length) {
|
|
92
|
+
console.log(newOverlappingDates)
|
|
93
|
+
}
|
|
94
|
+
|
|
78
95
|
const newDisableCalendarDates = {
|
|
79
96
|
...disableCalendarDates,
|
|
80
97
|
disabledDates: updatedDisabledDates,
|
|
@@ -90,5 +107,5 @@ export const useUpdateDisabledDates = ({
|
|
|
90
107
|
updateCalendarDefaultMonth,
|
|
91
108
|
])
|
|
92
109
|
|
|
93
|
-
return { newDisableCalendarDates, overlappingDate }
|
|
110
|
+
return { newDisableCalendarDates, overlappingDate, lastPossibleCheckout }
|
|
94
111
|
}
|
|
@@ -21,6 +21,7 @@ type Props = {
|
|
|
21
21
|
to: Date
|
|
22
22
|
}[]
|
|
23
23
|
disabledDates?: Matcher[]
|
|
24
|
+
lastPossibleCheckout?: Matcher
|
|
24
25
|
overlappingDate?: DateRange[]
|
|
25
26
|
rangeContext?: RangeContext
|
|
26
27
|
findFirstPossibleRangeContextCheckIn?: NonNullable<
|
|
@@ -48,6 +49,7 @@ export const handleCalendarModifiers = ({
|
|
|
48
49
|
findFirstPossibleRangeContextCheckIn,
|
|
49
50
|
findLastPossibleRangeContextCheckOut,
|
|
50
51
|
currentSelectionLastCheckoutDate,
|
|
52
|
+
lastPossibleCheckout,
|
|
51
53
|
}: Props) => {
|
|
52
54
|
// Parse data
|
|
53
55
|
const calendarRangeFrom = calendarRange?.from && endOfDay(calendarRange.from)
|
|
@@ -93,19 +95,19 @@ export const handleCalendarModifiers = ({
|
|
|
93
95
|
)
|
|
94
96
|
: []
|
|
95
97
|
|
|
96
|
-
console.log()
|
|
97
|
-
|
|
98
98
|
return {
|
|
99
99
|
booked: disabledDatesByPage.length
|
|
100
100
|
? disabledDatesByPage
|
|
101
101
|
: disabledDates?.length
|
|
102
102
|
? [
|
|
103
|
+
lastPossibleCheckout || [],
|
|
103
104
|
...disabledDates,
|
|
104
105
|
...firstPossibleRangeContextCheckIn,
|
|
105
106
|
...lastPossibleRangeContextCheckOut,
|
|
106
107
|
]
|
|
107
108
|
: newDisableCalendarDates?.disabledDates?.length
|
|
108
109
|
? [
|
|
110
|
+
lastPossibleCheckout || [],
|
|
109
111
|
...newDisableCalendarDates?.disabledDates,
|
|
110
112
|
...firstPossibleRangeContextCheckIn,
|
|
111
113
|
...lastPossibleRangeContextCheckOut,
|
|
@@ -8,5 +8,13 @@
|
|
|
8
8
|
"clearDates": "Clear dates",
|
|
9
9
|
"noCheckIn": "Room not available",
|
|
10
10
|
"noCheckOut": "Check-out not available",
|
|
11
|
-
"checkOutOnly": "Check-out only"
|
|
11
|
+
"checkOutOnly": "Check-out only",
|
|
12
|
+
"onlyCheckOut": "Check-in only",
|
|
13
|
+
"onlyCheckIn": "Check-out only",
|
|
14
|
+
"errors": {
|
|
15
|
+
"calendarErrors": {
|
|
16
|
+
"checkInAvailabilityError": "Check-in available for second room only with connection dates",
|
|
17
|
+
"checkInAvailabilityGuide": "Start or end day need connection for previous reservation"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
12
20
|
}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"endDate": "End date",
|
|
8
8
|
"title": "Calendar",
|
|
9
9
|
"checkoutOnly": "Check-out only",
|
|
10
|
-
"hasDisableDates": "Contains unavailable dates"
|
|
10
|
+
"hasDisableDates": "Contains unavailable dates",
|
|
11
|
+
"minNights": "2 nights min"
|
|
11
12
|
},
|
|
12
13
|
"guests": {
|
|
13
14
|
"label": "Number of guests",
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
"clearDates": "Tyhjennä",
|
|
9
9
|
"noCheckIn": "Huone ei saatavilla",
|
|
10
10
|
"noCheckOut": "Uloskirjaus ei saatavilla",
|
|
11
|
-
"checkOutOnly": "Vain uloskirjaus"
|
|
11
|
+
"checkOutOnly": "Vain uloskirjaus",
|
|
12
|
+
"onlyCheckOut": "Check-in only",
|
|
13
|
+
"onlyCheckIn": "Check-out only",
|
|
14
|
+
"errors": {
|
|
15
|
+
"calendarErrors": {
|
|
16
|
+
"checkInAvailabilityError": "Check-in available for second room only with connection dates",
|
|
17
|
+
"checkInAvailabilityGuide": "Start or end day need connection for previous reservation"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
12
20
|
}
|
|
13
21
|
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"endDate": "Loppu",
|
|
8
8
|
"title": "Kalenteri",
|
|
9
9
|
"checkoutOnly": "Check-out only",
|
|
10
|
-
"hasDisableDates": "Contains unavailable dates"
|
|
10
|
+
"hasDisableDates": "Contains unavailable dates",
|
|
11
|
+
"minNights": "2 nights min"
|
|
11
12
|
},
|
|
12
13
|
"guests": {
|
|
13
14
|
"label": "Vierasmäärä",
|