bukazu-portal-react 2.1.21 → 3.0.2
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/.github/workflows/dependabot.yml +11 -0
- package/.github/workflows/node.js.yml +31 -0
- package/.prettierrc +3 -6
- package/CHANGELOG.MD +5 -0
- package/babel.config.json +1 -1
- package/build/index.css +1 -2312
- package/build/portal.es.js +35483 -0
- package/build/portal.umd.js +596 -0
- package/{build/calendar.html → calendar.html} +2 -4
- package/coverage/clover.xml +28 -0
- package/coverage/coverage-final.json +2 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/helper.ts.html +142 -0
- package/coverage/lcov-report/index.html +116 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +36 -0
- package/cypress/{integration → e2e}/.examples/actions.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/aliasing.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/assertions.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/connectors.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/cookies.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/cypress_api.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/files.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/local_storage.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/location.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/misc.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/navigation.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/network_requests.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/querying.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/spies_stubs_clocks.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/traversal.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/utilities.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/viewport.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/waiting.spec.js +0 -0
- package/cypress/{integration → e2e}/.examples/window.spec.js +0 -0
- package/cypress/{integration → e2e}/booking.spec.js +0 -0
- package/cypress/{integration → e2e}/calendar.spec.js +0 -0
- package/cypress/{integration → e2e}/search.spec.js +0 -0
- package/cypress/support/commands.ts +37 -0
- package/cypress/support/component-index.html +12 -0
- package/cypress/support/component.ts +39 -0
- package/cypress/support/{index.js → e2e.js} +0 -0
- package/cypress.config.ts +15 -0
- package/{dev.js → dev.tsx} +1 -1
- package/index.html +15 -0
- package/{build/invalid-calendar.html → invalid-calendar.html} +0 -0
- package/jest.config.js +195 -0
- package/package.json +35 -40
- package/reviews.html +16 -0
- package/src/_lib/{SearchQueries.js → SearchQueries.ts} +8 -2
- package/src/_lib/{countries.js → countries.ts} +0 -0
- package/src/_lib/date_helper.ts +27 -0
- package/src/_lib/{queries.js → queries.ts} +24 -5
- package/src/components/App.tsx +132 -0
- package/src/components/AppContext.ts +14 -0
- package/src/components/CalendarPage/BookingForm.tsx +42 -0
- package/src/components/CalendarPage/Calendar.tsx +50 -0
- package/src/components/CalendarPage/CalendarPage.tsx +43 -0
- package/src/components/CalendarPage/CalendarParts/CalendarContext.tsx +89 -0
- package/src/components/CalendarPage/CalendarParts/CalendarHeader.tsx +72 -0
- package/src/components/CalendarPage/CalendarParts/DayClasses.ts +111 -0
- package/src/components/CalendarPage/CalendarParts/GenerateCalendar.tsx +64 -0
- package/src/components/CalendarPage/CalendarParts/Legend.tsx +33 -0
- package/src/components/CalendarPage/CalendarParts/MonthHeader.tsx +15 -0
- package/src/components/CalendarPage/CalendarParts/Months.tsx +37 -0
- package/src/components/CalendarPage/CalendarParts/RenderCells.tsx +94 -0
- package/src/components/CalendarPage/CalendarParts/SingleMonth.tsx +72 -0
- package/src/components/CalendarPage/CalendarParts/StartBooking.tsx +17 -0
- package/src/components/CalendarPage/CalendarParts/WeekDays.tsx +27 -0
- package/src/components/CalendarPage/FormCreator.tsx +213 -0
- package/src/components/CalendarPage/FormItems/{Date.js → Date.tsx} +10 -2
- package/src/components/CalendarPage/FormItems/{NumberSelect.js → NumberSelect.tsx} +1 -1
- package/src/components/CalendarPage/FormItems/{Select.js → Select.tsx} +0 -0
- package/src/components/CalendarPage/FormItems/{index.js → index.ts} +0 -0
- package/src/components/CalendarPage/PriceField/Price.tsx +58 -0
- package/src/components/CalendarPage/PriceField/Queries.ts +23 -0
- package/src/components/CalendarPage/PriceField/index.tsx +127 -0
- package/src/components/CalendarPage/Summary/{CostRow.js → CostRow.tsx} +19 -3
- package/src/components/CalendarPage/Summary/{CostSection.js → CostSection.tsx} +5 -1
- package/src/components/CalendarPage/Summary/{CostSummary.js → CostSummary.tsx} +19 -10
- package/src/components/CalendarPage/Summary/Description.tsx +27 -0
- package/src/components/CalendarPage/Summary/{InsurancesAndRequired.js → InsurancesAndRequired.tsx} +21 -2
- package/src/components/CalendarPage/Summary/Object.tsx +59 -0
- package/src/components/CalendarPage/Summary/{OnSite.js → OnSite.tsx} +9 -9
- package/src/components/CalendarPage/Summary/{OptionalNotOnSite.js → OptionalNotOnSite.tsx} +19 -18
- package/src/components/CalendarPage/Summary/{OptionalOnSite.js → OptionalOnSite.tsx} +6 -1
- package/src/components/CalendarPage/Summary/{Queries.js → Queries.ts} +3 -3
- package/src/components/CalendarPage/Summary/RentAndDiscount.tsx +30 -0
- package/src/components/CalendarPage/Summary/{Totals.js → Totals.tsx} +8 -3
- package/src/components/CalendarPage/Summary/cost_types.d.ts +31 -0
- package/src/components/CalendarPage/Summary/index.tsx +24 -0
- package/src/components/CalendarPage/calender_types.d.ts +16 -0
- package/src/components/CalendarPage/formParts/AssistanceMessage.tsx +60 -0
- package/src/components/CalendarPage/formParts/{BookingHelpers.js → BookingHelpers.tsx} +3 -3
- package/src/components/CalendarPage/formParts/{BookingOrOption.js → BookingOrOption.tsx} +6 -1
- package/src/components/CalendarPage/formParts/CancelInsuranceText.tsx +105 -0
- package/src/components/CalendarPage/formParts/{DefaultBookingFields.js → DefaultBookingFields.ts} +3 -1
- package/src/components/CalendarPage/formParts/DiscountCode.tsx +62 -0
- package/src/components/CalendarPage/formParts/{Guests.js → Guests.tsx} +10 -4
- package/src/components/CalendarPage/formParts/{OptionalBookingFields.js → OptionalBookingFields.tsx} +0 -0
- package/src/components/CalendarPage/formParts/{OptionalCosts.js → OptionalCosts.tsx} +1 -2
- package/src/components/CalendarPage/formParts/{SuccessMessage.js → SuccessMessage.tsx} +0 -0
- package/src/components/CalendarPage/formParts/Validations.tsx +78 -0
- package/src/components/CalendarPage/formParts/{discount.js → discount.tsx} +11 -2
- package/src/components/CalendarPage/formParts/form_types.d.ts +38 -0
- package/src/components/CalendarPage/formParts/{insurances.js → insurances.tsx} +14 -10
- package/src/components/CalendarPage/formParts/{radioButtons.js → radioButtons.tsx} +0 -0
- package/src/components/Error/{ApiError.js → ApiError.tsx} +6 -4
- package/src/components/Error/{IntegrationError.js → IntegrationError.tsx} +17 -11
- package/src/components/Error/{index.js → index.ts} +0 -0
- package/src/components/{ErrorBoundary.js → ErrorBoundary.tsx} +13 -5
- package/src/components/Modal/index.tsx +46 -0
- package/src/components/ReviewsPage/Queries.ts +26 -0
- package/src/components/ReviewsPage/ReviewsPage.tsx +43 -0
- package/src/components/ReviewsPage/Score.tsx +25 -0
- package/src/components/ReviewsPage/SingleReview.tsx +38 -0
- package/src/components/SafeBooking.tsx +97 -0
- package/src/components/SearchPage/Field.tsx +75 -0
- package/src/components/SearchPage/Filters.tsx +91 -0
- package/src/components/SearchPage/Paginator.tsx +63 -0
- package/src/components/SearchPage/Results.tsx +129 -0
- package/src/components/SearchPage/{SearchPage.js → SearchPage.tsx} +42 -31
- package/src/components/SearchPage/{SingleResult.js → SingleResult.tsx} +15 -8
- package/src/components/SearchPage/filters/Categories.tsx +57 -0
- package/src/components/SearchPage/filters/DateFilter.tsx +34 -0
- package/src/components/SearchPage/filters/List.tsx +80 -0
- package/src/components/SearchPage/filters/NumberFilter.tsx +37 -0
- package/src/components/SearchPage/filters/Radio.tsx +46 -0
- package/src/components/SearchPage/filters/Select.tsx +85 -0
- package/src/components/SearchPage/filters/__tests__/helper.spec.js +15 -0
- package/src/components/SearchPage/filters/filter_types.d.ts +25 -0
- package/src/components/SearchPage/filters/helper.ts +19 -0
- package/src/components/icons/ArrowLeft.svg.tsx +20 -0
- package/src/components/icons/{ArrowRight.svg.js → ArrowRight.svg.tsx} +0 -0
- package/src/components/icons/{Reload.svg.js → Reload.svg.tsx} +0 -0
- package/src/components/icons/{info.svg.js → info.svg.tsx} +0 -0
- package/src/components/icons/{loading.svg.js → loading.svg.tsx} +1 -1
- package/src/custom.d.ts +10 -0
- package/src/index.tsx +93 -0
- package/src/locales/de.json +4 -3
- package/src/locales/en.json +4 -3
- package/src/locales/es.json +4 -3
- package/src/locales/fr.json +4 -3
- package/src/locales/it.json +4 -3
- package/src/locales/nl.json +4 -3
- package/src/styles/main.css +2 -1
- package/src/styles/modal.css +1 -1
- package/src/styles/pagination.css +25 -23
- package/src/styles/result.css +33 -2
- package/src/styles/reviews.css +76 -0
- package/src/types.d.ts +85 -0
- package/tsconfig.json +17 -0
- package/vite.config.ts +31 -0
- package/build/index.html +0 -16
- package/build/index.js +0 -48528
- package/cypress.json +0 -9
- package/rollup.config.js +0 -30
- package/src/_lib/format.js +0 -16
- package/src/components/App.js +0 -164
- package/src/components/CalendarPage/BookingForm.js +0 -57
- package/src/components/CalendarPage/Calendar.js +0 -373
- package/src/components/CalendarPage/CalendarHeader.js +0 -58
- package/src/components/CalendarPage/CalendarPage.js +0 -158
- package/src/components/CalendarPage/FormCreator.js +0 -278
- package/src/components/CalendarPage/FormItems/Wrapper.js +0 -0
- package/src/components/CalendarPage/PriceField.js +0 -203
- package/src/components/CalendarPage/Summary/Description.js +0 -22
- package/src/components/CalendarPage/Summary/Object.js +0 -46
- package/src/components/CalendarPage/Summary/RentAndDiscount.js +0 -21
- package/src/components/CalendarPage/Summary/index.js +0 -19
- package/src/components/CalendarPage/formParts/AssistanceMessage.js +0 -47
- package/src/components/CalendarPage/formParts/CancelInsuranceText.js +0 -91
- package/src/components/CalendarPage/formParts/DiscountCode.js +0 -62
- package/src/components/CalendarPage/formParts/summary.js +0 -43
- package/src/components/Modal/index.js +0 -58
- package/src/components/ReviewsPage/ReviewsPage.js +0 -15
- package/src/components/SafeBooking.js +0 -69
- package/src/components/SearchPage/Field.js +0 -241
- package/src/components/SearchPage/Filters.js +0 -108
- package/src/components/SearchPage/Paginator.js +0 -59
- package/src/components/SearchPage/Results.js +0 -130
- package/src/components/SearchPage/filters/List.js +0 -63
- package/src/components/icons/ArrowLeft.svg.js +0 -18
- package/src/index.js +0 -74
- package/webpack.config.dev.js +0 -53
- package/webpack.config.js +0 -67
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { addMonths, subMonths } from 'date-fns';
|
|
3
|
+
import CalendarHeader from './CalendarParts/CalendarHeader';
|
|
4
|
+
import AssistanceMessage from './formParts/AssistanceMessage';
|
|
5
|
+
import Legend from './CalendarParts/Legend';
|
|
6
|
+
import Months from './CalendarParts/Months';
|
|
7
|
+
import StartBooking from './CalendarParts/StartBooking';
|
|
8
|
+
import { HouseType } from '../../types';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
numberOfMonths: number;
|
|
12
|
+
numberOfMonthsInARow: number;
|
|
13
|
+
house: HouseType;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function Calendar({
|
|
17
|
+
numberOfMonths,
|
|
18
|
+
house,
|
|
19
|
+
numberOfMonthsInARow
|
|
20
|
+
}: Props): JSX.Element {
|
|
21
|
+
const [currentMonth, setCurrentMonth] = useState(new Date());
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="calendar-container ">
|
|
25
|
+
<StartBooking house={house} />
|
|
26
|
+
<div className="calendar-section">
|
|
27
|
+
<CalendarHeader
|
|
28
|
+
changeMonth={setCurrentMonth}
|
|
29
|
+
currentMonth={currentMonth}
|
|
30
|
+
numberOfMonths={numberOfMonths}
|
|
31
|
+
/>
|
|
32
|
+
<Months
|
|
33
|
+
house={house}
|
|
34
|
+
numberOfMonths={numberOfMonths}
|
|
35
|
+
numberOfMonthsInARow={numberOfMonthsInARow}
|
|
36
|
+
currentMonth={currentMonth}
|
|
37
|
+
/>
|
|
38
|
+
<Legend house={house} />
|
|
39
|
+
<AssistanceMessage house={house} />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Calendar.defaultProps = {
|
|
46
|
+
numberOfMonths: 4,
|
|
47
|
+
numberOfMonthsInARow: 2
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default Calendar;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import BookingForm from './BookingForm';
|
|
3
|
+
import GenerateCalendar from './CalendarParts/GenerateCalendar';
|
|
4
|
+
import { PortalSiteType } from '../../types';
|
|
5
|
+
import {
|
|
6
|
+
CalendarContext,
|
|
7
|
+
CalendarProvider
|
|
8
|
+
} from './CalendarParts/CalendarContext';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
PortalSite: PortalSiteType;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function CalendarPage({ PortalSite }: Props): JSX.Element {
|
|
15
|
+
const { bookingStarted } = useContext(CalendarContext);
|
|
16
|
+
|
|
17
|
+
if (bookingStarted) {
|
|
18
|
+
return <BookingForm />;
|
|
19
|
+
} else {
|
|
20
|
+
return <GenerateCalendar PortalSite={PortalSite} />;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
CalendarPage.DefaultProps = {
|
|
25
|
+
PortalSite: {
|
|
26
|
+
options: {
|
|
27
|
+
bookingForm: {
|
|
28
|
+
numberOfMonths: 4,
|
|
29
|
+
numberOfMonthsInARow: 2
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function CalendarWrapper({ PortalSite }: Props): JSX.Element {
|
|
36
|
+
return (
|
|
37
|
+
<CalendarProvider>
|
|
38
|
+
<CalendarPage PortalSite={PortalSite} />
|
|
39
|
+
</CalendarProvider>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default CalendarWrapper;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { differenceInCalendarDays, isAfter } from 'date-fns';
|
|
2
|
+
import React, { createContext, useReducer } from 'react';
|
|
3
|
+
import { BuDate, HouseType } from '../../../types';
|
|
4
|
+
import { Parse_EN_US } from '../../../_lib/date_helper';
|
|
5
|
+
import { BookingType } from '../calender_types';
|
|
6
|
+
|
|
7
|
+
const initialBooking: BookingType = {
|
|
8
|
+
selectedDate: null,
|
|
9
|
+
arrivalDate: null,
|
|
10
|
+
departureDate: null,
|
|
11
|
+
bookingStarted: false,
|
|
12
|
+
persons: 2
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const CalendarContext = createContext<BookingType>(initialBooking);
|
|
16
|
+
export const CalendarContextDispatch = createContext<Function>(calendarReducer);
|
|
17
|
+
|
|
18
|
+
export function CalendarProvider({
|
|
19
|
+
children
|
|
20
|
+
}: {
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
}): React.ReactNode {
|
|
23
|
+
const [booking_state, dispatch] = useReducer(calendarReducer, initialBooking);
|
|
24
|
+
return (
|
|
25
|
+
<CalendarContext.Provider value={booking_state}>
|
|
26
|
+
<CalendarContextDispatch.Provider value={dispatch}>
|
|
27
|
+
{children}
|
|
28
|
+
</CalendarContextDispatch.Provider>
|
|
29
|
+
</CalendarContext.Provider>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function calendarReducer(
|
|
34
|
+
bookingState: BookingType,
|
|
35
|
+
action: { type: string; house: HouseType; day: BuDate; persons: number }
|
|
36
|
+
): BookingType {
|
|
37
|
+
console.log({ action });
|
|
38
|
+
switch (action.type) {
|
|
39
|
+
case 'clicked': {
|
|
40
|
+
const { day, house } = action;
|
|
41
|
+
const { selectedDate, arrivalDate } = bookingState;
|
|
42
|
+
const date = Parse_EN_US(day.date);
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
day.departure &&
|
|
46
|
+
selectedDate &&
|
|
47
|
+
arrivalDate &&
|
|
48
|
+
isAfter(date, selectedDate) &&
|
|
49
|
+
differenceInCalendarDays(date, selectedDate) <= house.max_nights &&
|
|
50
|
+
differenceInCalendarDays(date, selectedDate) >=
|
|
51
|
+
arrivalDate.min_nights &&
|
|
52
|
+
differenceInCalendarDays(date, selectedDate) <= arrivalDate.max_nights
|
|
53
|
+
) {
|
|
54
|
+
return {
|
|
55
|
+
...bookingState,
|
|
56
|
+
departureDate: day
|
|
57
|
+
};
|
|
58
|
+
} else if (day.arrival) {
|
|
59
|
+
return {
|
|
60
|
+
bookingStarted: false,
|
|
61
|
+
selectedDate: date,
|
|
62
|
+
arrivalDate: day,
|
|
63
|
+
departureDate: null,
|
|
64
|
+
persons: 2
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return bookingState;
|
|
68
|
+
}
|
|
69
|
+
case 'reset': {
|
|
70
|
+
return initialBooking;
|
|
71
|
+
}
|
|
72
|
+
case 'start': {
|
|
73
|
+
return {
|
|
74
|
+
...bookingState,
|
|
75
|
+
bookingStarted: true,
|
|
76
|
+
persons: action.persons
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
case 'return': {
|
|
80
|
+
return {
|
|
81
|
+
...bookingState,
|
|
82
|
+
bookingStarted: false
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
default: {
|
|
86
|
+
throw Error('Unknown action: ' + action.type);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import ArrowRight from '../../icons/ArrowRight.svg';
|
|
3
|
+
import ArrowLeft from '../../icons/ArrowLeft.svg';
|
|
4
|
+
import Reload from '../../icons/Reload.svg';
|
|
5
|
+
import { CalendarContextDispatch } from './CalendarContext';
|
|
6
|
+
import { addMonths, subMonths } from 'date-fns';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
changeMonth: Function;
|
|
10
|
+
currentMonth: Date;
|
|
11
|
+
numberOfMonths: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function CalendarHeader({
|
|
15
|
+
changeMonth,
|
|
16
|
+
currentMonth,
|
|
17
|
+
numberOfMonths
|
|
18
|
+
}: Props): JSX.Element {
|
|
19
|
+
const dispatch = useContext(CalendarContextDispatch);
|
|
20
|
+
|
|
21
|
+
function next() {
|
|
22
|
+
changeMonth(addMonths(currentMonth, numberOfMonths));
|
|
23
|
+
}
|
|
24
|
+
function prev() {
|
|
25
|
+
changeMonth(subMonths(currentMonth, numberOfMonths));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div className="calendars-header">
|
|
30
|
+
<div
|
|
31
|
+
className="col bu-prev"
|
|
32
|
+
style={{ textAlign: 'center' }}
|
|
33
|
+
onClick={prev}
|
|
34
|
+
tabIndex={0}
|
|
35
|
+
role="button"
|
|
36
|
+
>
|
|
37
|
+
<div className="icon">
|
|
38
|
+
{' '}
|
|
39
|
+
<ArrowLeft />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<div
|
|
43
|
+
className="col bu-reset"
|
|
44
|
+
onClick={() => {
|
|
45
|
+
dispatch({
|
|
46
|
+
type: 'reset'
|
|
47
|
+
});
|
|
48
|
+
}}
|
|
49
|
+
style={{ textAlign: 'center' }}
|
|
50
|
+
tabIndex={0}
|
|
51
|
+
role="button"
|
|
52
|
+
>
|
|
53
|
+
<div className="icon">
|
|
54
|
+
<Reload />
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div
|
|
58
|
+
className="col bu-next"
|
|
59
|
+
onClick={next}
|
|
60
|
+
style={{ textAlign: 'center' }}
|
|
61
|
+
tabIndex={0}
|
|
62
|
+
role="button"
|
|
63
|
+
>
|
|
64
|
+
<div className="icon">
|
|
65
|
+
<ArrowRight />
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default CalendarHeader;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addDays,
|
|
3
|
+
differenceInCalendarDays,
|
|
4
|
+
isAfter,
|
|
5
|
+
isBefore,
|
|
6
|
+
isSameDay,
|
|
7
|
+
isSameMonth,
|
|
8
|
+
subDays
|
|
9
|
+
} from 'date-fns';
|
|
10
|
+
import { BuDate } from '../../../types';
|
|
11
|
+
import { Parse_EN_US } from '../../../_lib/date_helper';
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
day: Date;
|
|
15
|
+
monthStart: Date;
|
|
16
|
+
prevBooked: BuDate;
|
|
17
|
+
buDate: BuDate;
|
|
18
|
+
dates: {
|
|
19
|
+
selectedDate: Date;
|
|
20
|
+
departureDate: BuDate;
|
|
21
|
+
arrivalDate: BuDate;
|
|
22
|
+
};
|
|
23
|
+
house: {
|
|
24
|
+
max_nights: Number;
|
|
25
|
+
};
|
|
26
|
+
discounts: [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function DayClasses({
|
|
30
|
+
day,
|
|
31
|
+
monthStart,
|
|
32
|
+
buDate,
|
|
33
|
+
prevBooked,
|
|
34
|
+
dates,
|
|
35
|
+
house,
|
|
36
|
+
discounts
|
|
37
|
+
}: Props): string {
|
|
38
|
+
const { selectedDate, departureDate, arrivalDate } = dates;
|
|
39
|
+
let classes = ['col', 'cell'];
|
|
40
|
+
|
|
41
|
+
if (!isSameMonth(day, monthStart)) {
|
|
42
|
+
classes.push('disabled');
|
|
43
|
+
return classes.join(' ');
|
|
44
|
+
}
|
|
45
|
+
if (buDate) {
|
|
46
|
+
if (buDate.arrival && isAfter(day, new Date()) && buDate.max_nights !== 0) {
|
|
47
|
+
if (prevBooked.max_nights === 0) {
|
|
48
|
+
classes.push('departure-arrival');
|
|
49
|
+
} else {
|
|
50
|
+
classes.push('arrival');
|
|
51
|
+
classes.push('arrival');
|
|
52
|
+
}
|
|
53
|
+
} else if (buDate.max_nights === 0) {
|
|
54
|
+
if (prevBooked.max_nights !== 0) {
|
|
55
|
+
classes.push('booked-departure');
|
|
56
|
+
} else {
|
|
57
|
+
classes.push('booked');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (selectedDate) {
|
|
63
|
+
if (isSameDay(day, selectedDate)) {
|
|
64
|
+
classes.push('selected');
|
|
65
|
+
}
|
|
66
|
+
const minimum =
|
|
67
|
+
differenceInCalendarDays(day, selectedDate) >= arrivalDate.min_nights;
|
|
68
|
+
const maximum =
|
|
69
|
+
differenceInCalendarDays(day, selectedDate) <= house.max_nights &&
|
|
70
|
+
differenceInCalendarDays(day, selectedDate) <= arrivalDate.max_nights;
|
|
71
|
+
|
|
72
|
+
if (
|
|
73
|
+
buDate.departure &&
|
|
74
|
+
isAfter(day, selectedDate) &&
|
|
75
|
+
minimum &&
|
|
76
|
+
maximum &&
|
|
77
|
+
prevBooked.max_nights !== 0
|
|
78
|
+
) {
|
|
79
|
+
classes.push('departure');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (departureDate) {
|
|
84
|
+
if (
|
|
85
|
+
isAfter(day, selectedDate) &&
|
|
86
|
+
isBefore(day, Parse_EN_US(departureDate.date))
|
|
87
|
+
) {
|
|
88
|
+
classes.push('selected');
|
|
89
|
+
}
|
|
90
|
+
if (isSameDay(day, Parse_EN_US(departureDate.date))) {
|
|
91
|
+
classes.push('selected');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const daysFromToday = differenceInCalendarDays(day, new Date());
|
|
96
|
+
const last_minute =
|
|
97
|
+
daysFromToday <= house.last_minute_days && daysFromToday > 0;
|
|
98
|
+
|
|
99
|
+
const discount = discounts.find(
|
|
100
|
+
(x) =>
|
|
101
|
+
isBefore(subDays(Parse_EN_US(x.discount_starts_at), 1), day) &&
|
|
102
|
+
isAfter(addDays(Parse_EN_US(x.discount_ends_at), 1), day)
|
|
103
|
+
);
|
|
104
|
+
if (last_minute || discount || buDate.special_offer > 0) {
|
|
105
|
+
classes.push('discount');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return classes.join(' ');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default DayClasses;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useQuery } from '@apollo/client';
|
|
2
|
+
import React, { useContext } from 'react';
|
|
3
|
+
import { FormattedMessage } from 'react-intl';
|
|
4
|
+
import { HouseType, PortalSiteType } from '../../../types';
|
|
5
|
+
import { SINGLE_HOUSE_QUERY } from '../../../_lib/queries';
|
|
6
|
+
import { AppContext } from '../../AppContext';
|
|
7
|
+
import { ApiError } from '../../Error';
|
|
8
|
+
import Loading from '../../icons/loading.svg';
|
|
9
|
+
import Calendar from '../Calendar';
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
PortalSite: PortalSiteType;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function GenerateCalendar({ PortalSite }: Props): JSX.Element {
|
|
16
|
+
const { portalCode, objectCode } = useContext(AppContext);
|
|
17
|
+
const { loading, error, data } = useQuery(SINGLE_HOUSE_QUERY, {
|
|
18
|
+
variables: { portalCode, objectCode }
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (loading)
|
|
22
|
+
return (
|
|
23
|
+
<div>
|
|
24
|
+
<Loading />
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
if (error) {
|
|
28
|
+
return (
|
|
29
|
+
<div>
|
|
30
|
+
<ApiError errors={error} />
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const Results = data.PortalSite.houses;
|
|
36
|
+
const numberOfMonths = PortalSite.options.bookingForm
|
|
37
|
+
? PortalSite.options.bookingForm.numberOfMonths
|
|
38
|
+
: 4;
|
|
39
|
+
const numberOfMonthsInARow = PortalSite.options.bookingForm
|
|
40
|
+
? PortalSite.options.bookingForm.numberOfMonthsInARow
|
|
41
|
+
: 4;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div id="calendar-container">
|
|
45
|
+
{Results.length === 0 && (
|
|
46
|
+
<div>
|
|
47
|
+
<FormattedMessage id="no_house_found" />
|
|
48
|
+
</div>
|
|
49
|
+
)}
|
|
50
|
+
{Results.map((result: HouseType) => (
|
|
51
|
+
<div key={result.id}>
|
|
52
|
+
<div className="house-name">{result.name}</div>
|
|
53
|
+
<Calendar
|
|
54
|
+
numberOfMonths={numberOfMonths}
|
|
55
|
+
numberOfMonthsInARow={numberOfMonthsInARow}
|
|
56
|
+
house={result}
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
))}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default GenerateCalendar;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormattedMessage } from 'react-intl';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
house: {
|
|
6
|
+
house_type: string;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function Legend({ house }: Props): JSX.Element {
|
|
11
|
+
return (
|
|
12
|
+
<div className="legend">
|
|
13
|
+
<div>
|
|
14
|
+
<span className="legend-field arrival" />
|
|
15
|
+
<FormattedMessage id={`${house.house_type}.arrival_date`} />
|
|
16
|
+
</div>
|
|
17
|
+
<div>
|
|
18
|
+
<span className="legend-field booked" />
|
|
19
|
+
<FormattedMessage id="booked" />
|
|
20
|
+
</div>
|
|
21
|
+
<div>
|
|
22
|
+
<span className="legend-field departure" />
|
|
23
|
+
<FormattedMessage id={`${house.house_type}.departure_date`} />
|
|
24
|
+
</div>
|
|
25
|
+
<div>
|
|
26
|
+
<span className="legend-field last_minute_discount" />
|
|
27
|
+
<FormattedMessage id="discount" />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default Legend;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { MONTH_FORMAT, FormatIntl } from '../../../_lib/date_helper';
|
|
3
|
+
|
|
4
|
+
interface Props { month: Date; }
|
|
5
|
+
|
|
6
|
+
const MonthHeader = ({ month }:Props): JSX.Element => (
|
|
7
|
+
<div className="header row flex-middle">
|
|
8
|
+
<div className="col col-center" style={{ textAlign: 'center' }}>
|
|
9
|
+
<span>{FormatIntl(month, MONTH_FORMAT)}</span>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export default MonthHeader
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { addMonths, format } from 'date-fns';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { HouseType } from '../../../types';
|
|
4
|
+
import SingleMonth from './SingleMonth';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
house: HouseType;
|
|
8
|
+
numberOfMonths: number;
|
|
9
|
+
numberOfMonthsInARow: number;
|
|
10
|
+
currentMonth: Date;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function Months({
|
|
14
|
+
numberOfMonthsInARow,
|
|
15
|
+
currentMonth,
|
|
16
|
+
numberOfMonths,
|
|
17
|
+
|
|
18
|
+
house
|
|
19
|
+
}: Props): JSX.Element {
|
|
20
|
+
let template: JSX.Element[] = [];
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < numberOfMonths; i++) {
|
|
23
|
+
template.push(
|
|
24
|
+
<SingleMonth
|
|
25
|
+
key={format(addMonths(currentMonth, i), 'MM-yyyy')}
|
|
26
|
+
house={house}
|
|
27
|
+
numberOfMonthsInARow={numberOfMonthsInARow}
|
|
28
|
+
currentMonth={currentMonth}
|
|
29
|
+
count={i}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return <div className="calendars-row">{template}</div>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default Months;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
addDays,
|
|
4
|
+
endOfMonth,
|
|
5
|
+
endOfWeek,
|
|
6
|
+
startOfMonth,
|
|
7
|
+
startOfWeek,
|
|
8
|
+
subDays,
|
|
9
|
+
isBefore,
|
|
10
|
+
isAfter,
|
|
11
|
+
differenceInCalendarDays,
|
|
12
|
+
addMonths,
|
|
13
|
+
isSameMonth,
|
|
14
|
+
parse,
|
|
15
|
+
isSameDay
|
|
16
|
+
} from 'date-fns';
|
|
17
|
+
import { FormatIntl, Parse_EN_US } from '../../../_lib/date_helper';
|
|
18
|
+
import DayClasses from './DayClasses';
|
|
19
|
+
import { HouseType } from '../../../types';
|
|
20
|
+
import { CalendarContext, CalendarContextDispatch } from './CalendarContext';
|
|
21
|
+
|
|
22
|
+
interface CellProps {
|
|
23
|
+
availabilities: [];
|
|
24
|
+
month: Date;
|
|
25
|
+
discounts: [];
|
|
26
|
+
house: HouseType;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function RenderCells({
|
|
30
|
+
availabilities,
|
|
31
|
+
month,
|
|
32
|
+
discounts,
|
|
33
|
+
house
|
|
34
|
+
}: CellProps): JSX.Element {
|
|
35
|
+
const dispatch = useContext(CalendarContextDispatch);
|
|
36
|
+
const dates = useContext(CalendarContext);
|
|
37
|
+
|
|
38
|
+
const monthStart = startOfMonth(month);
|
|
39
|
+
const monthEnd = endOfMonth(monthStart);
|
|
40
|
+
const startDate = startOfWeek(monthStart);
|
|
41
|
+
const endDate = endOfWeek(monthEnd);
|
|
42
|
+
const rows: JSX.Element[] = [];
|
|
43
|
+
|
|
44
|
+
let days: JSX.Element[] = [];
|
|
45
|
+
let day: Date = startDate;
|
|
46
|
+
|
|
47
|
+
while (day <= endDate) {
|
|
48
|
+
// for (let daz of dayz) {
|
|
49
|
+
for (let i = 0; i < 7; i++) {
|
|
50
|
+
let date = FormatIntl(day, 'yyyy-MM-dd');
|
|
51
|
+
let yesterday = FormatIntl(subDays(day, 1), 'yyyy-MM-dd');
|
|
52
|
+
let daz = availabilities.find((x) => x.date === date);
|
|
53
|
+
|
|
54
|
+
const prevBooked = availabilities.find((x) => x.date === yesterday);
|
|
55
|
+
const cloneDay = daz;
|
|
56
|
+
|
|
57
|
+
days.push(
|
|
58
|
+
<div
|
|
59
|
+
className={DayClasses({
|
|
60
|
+
day,
|
|
61
|
+
monthStart,
|
|
62
|
+
discounts,
|
|
63
|
+
buDate: daz,
|
|
64
|
+
prevBooked,
|
|
65
|
+
house,
|
|
66
|
+
dates
|
|
67
|
+
})}
|
|
68
|
+
key={daz.date}
|
|
69
|
+
role="button"
|
|
70
|
+
tabIndex={0}
|
|
71
|
+
onClick={() => {
|
|
72
|
+
dispatch({
|
|
73
|
+
type: 'clicked',
|
|
74
|
+
day: cloneDay,
|
|
75
|
+
house
|
|
76
|
+
});
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
<span>{FormatIntl(day, 'd')}</span>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
day = addDays(day, 1);
|
|
83
|
+
}
|
|
84
|
+
rows.push(
|
|
85
|
+
<div className="row" key={day}>
|
|
86
|
+
{days}
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
days = [];
|
|
90
|
+
}
|
|
91
|
+
return <div className="body">{rows}</div>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default RenderCells;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useQuery } from '@apollo/client';
|
|
2
|
+
import {
|
|
3
|
+
addMonths,
|
|
4
|
+
endOfMonth,
|
|
5
|
+
endOfWeek,
|
|
6
|
+
startOfMonth,
|
|
7
|
+
startOfWeek
|
|
8
|
+
} from 'date-fns';
|
|
9
|
+
import React, { useContext } from 'react';
|
|
10
|
+
import { HouseType } from '../../../types';
|
|
11
|
+
import { CALENDAR_QUERY } from '../../../_lib/queries';
|
|
12
|
+
import { AppContext } from '../../AppContext';
|
|
13
|
+
import Loading from '../../icons/loading.svg';
|
|
14
|
+
import MonthHeader from './MonthHeader';
|
|
15
|
+
import RenderCells from './RenderCells';
|
|
16
|
+
import WeekDays from './WeekDays';
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
count: number;
|
|
20
|
+
currentMonth: Date;
|
|
21
|
+
house: HouseType;
|
|
22
|
+
numberOfMonthsInARow: Number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function SingleMonth({
|
|
26
|
+
count,
|
|
27
|
+
currentMonth,
|
|
28
|
+
house,
|
|
29
|
+
numberOfMonthsInARow
|
|
30
|
+
}: Props): JSX.Element {
|
|
31
|
+
const { portalCode, objectCode } = useContext(AppContext);
|
|
32
|
+
|
|
33
|
+
let month = addMonths(currentMonth, count);
|
|
34
|
+
let monthStart = startOfMonth(month);
|
|
35
|
+
let monthEnd = endOfMonth(month);
|
|
36
|
+
const variables = {
|
|
37
|
+
id: portalCode,
|
|
38
|
+
house_id: objectCode,
|
|
39
|
+
starts_at: startOfWeek(monthStart),
|
|
40
|
+
ends_at: endOfWeek(monthEnd)
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const { loading, error, data } = useQuery(CALENDAR_QUERY, { variables });
|
|
44
|
+
|
|
45
|
+
if (loading)
|
|
46
|
+
return (
|
|
47
|
+
<div>
|
|
48
|
+
<Loading />
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
if (error) {
|
|
52
|
+
return <div>Error</div>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const results = data.PortalSite.houses[0].availabilities;
|
|
56
|
+
const discounts = data.Discounts;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className={`calendar calendar-${numberOfMonthsInARow}`} key={month}>
|
|
60
|
+
<MonthHeader month={month} />
|
|
61
|
+
<WeekDays month={month} />
|
|
62
|
+
<RenderCells
|
|
63
|
+
availabilities={results}
|
|
64
|
+
discounts={discounts}
|
|
65
|
+
month={month}
|
|
66
|
+
house={house}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default SingleMonth;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { HouseType } from '../../../types';
|
|
3
|
+
import PriceField from '../PriceField';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
house: HouseType;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function StartBooking({ house }: Props): JSX.Element {
|
|
10
|
+
return (
|
|
11
|
+
<div className="price-overview">
|
|
12
|
+
<PriceField house={house} />
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default StartBooking;
|