bukazu-portal-react 2.1.21 → 3.0.3

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.
Files changed (191) hide show
  1. package/.github/workflows/dependabot.yml +11 -0
  2. package/.github/workflows/node.js.yml +31 -0
  3. package/.prettierrc +3 -6
  4. package/CHANGELOG.MD +5 -0
  5. package/babel.config.json +1 -1
  6. package/build/index.css +1 -2312
  7. package/build/portal.es.js +35483 -0
  8. package/build/portal.umd.js +596 -0
  9. package/{build/calendar.html → calendar.html} +2 -4
  10. package/coverage/clover.xml +28 -0
  11. package/coverage/coverage-final.json +2 -0
  12. package/coverage/lcov-report/base.css +224 -0
  13. package/coverage/lcov-report/block-navigation.js +87 -0
  14. package/coverage/lcov-report/favicon.png +0 -0
  15. package/coverage/lcov-report/helper.ts.html +142 -0
  16. package/coverage/lcov-report/index.html +116 -0
  17. package/coverage/lcov-report/prettify.css +1 -0
  18. package/coverage/lcov-report/prettify.js +2 -0
  19. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  20. package/coverage/lcov-report/sorter.js +196 -0
  21. package/coverage/lcov.info +36 -0
  22. package/cypress/{integration → e2e}/.examples/actions.spec.js +0 -0
  23. package/cypress/{integration → e2e}/.examples/aliasing.spec.js +0 -0
  24. package/cypress/{integration → e2e}/.examples/assertions.spec.js +0 -0
  25. package/cypress/{integration → e2e}/.examples/connectors.spec.js +0 -0
  26. package/cypress/{integration → e2e}/.examples/cookies.spec.js +0 -0
  27. package/cypress/{integration → e2e}/.examples/cypress_api.spec.js +0 -0
  28. package/cypress/{integration → e2e}/.examples/files.spec.js +0 -0
  29. package/cypress/{integration → e2e}/.examples/local_storage.spec.js +0 -0
  30. package/cypress/{integration → e2e}/.examples/location.spec.js +0 -0
  31. package/cypress/{integration → e2e}/.examples/misc.spec.js +0 -0
  32. package/cypress/{integration → e2e}/.examples/navigation.spec.js +0 -0
  33. package/cypress/{integration → e2e}/.examples/network_requests.spec.js +0 -0
  34. package/cypress/{integration → e2e}/.examples/querying.spec.js +0 -0
  35. package/cypress/{integration → e2e}/.examples/spies_stubs_clocks.spec.js +0 -0
  36. package/cypress/{integration → e2e}/.examples/traversal.spec.js +0 -0
  37. package/cypress/{integration → e2e}/.examples/utilities.spec.js +0 -0
  38. package/cypress/{integration → e2e}/.examples/viewport.spec.js +0 -0
  39. package/cypress/{integration → e2e}/.examples/waiting.spec.js +0 -0
  40. package/cypress/{integration → e2e}/.examples/window.spec.js +0 -0
  41. package/cypress/{integration → e2e}/booking.spec.js +0 -0
  42. package/cypress/{integration → e2e}/calendar.spec.js +0 -0
  43. package/cypress/{integration → e2e}/search.spec.js +0 -0
  44. package/cypress/support/commands.ts +37 -0
  45. package/cypress/support/component-index.html +12 -0
  46. package/cypress/support/component.ts +39 -0
  47. package/cypress/support/{index.js → e2e.js} +0 -0
  48. package/cypress.config.ts +15 -0
  49. package/{dev.js → dev.tsx} +1 -1
  50. package/index.html +15 -0
  51. package/{build/invalid-calendar.html → invalid-calendar.html} +0 -0
  52. package/jest.config.js +195 -0
  53. package/package.json +35 -40
  54. package/reviews.html +16 -0
  55. package/src/_lib/{SearchQueries.js → SearchQueries.ts} +8 -2
  56. package/src/_lib/{countries.js → countries.ts} +0 -0
  57. package/src/_lib/date_helper.ts +27 -0
  58. package/src/_lib/{queries.js → queries.ts} +24 -5
  59. package/src/components/App.tsx +132 -0
  60. package/src/components/AppContext.ts +14 -0
  61. package/src/components/CalendarPage/BookingForm.tsx +42 -0
  62. package/src/components/CalendarPage/Calendar.tsx +50 -0
  63. package/src/components/CalendarPage/CalendarPage.tsx +43 -0
  64. package/src/components/CalendarPage/CalendarParts/CalendarContext.tsx +89 -0
  65. package/src/components/CalendarPage/CalendarParts/CalendarHeader.tsx +72 -0
  66. package/src/components/CalendarPage/CalendarParts/DayClasses.ts +111 -0
  67. package/src/components/CalendarPage/CalendarParts/GenerateCalendar.tsx +64 -0
  68. package/src/components/CalendarPage/CalendarParts/Legend.tsx +33 -0
  69. package/src/components/CalendarPage/CalendarParts/MonthHeader.tsx +15 -0
  70. package/src/components/CalendarPage/CalendarParts/Months.tsx +37 -0
  71. package/src/components/CalendarPage/CalendarParts/RenderCells.tsx +94 -0
  72. package/src/components/CalendarPage/CalendarParts/SingleMonth.tsx +72 -0
  73. package/src/components/CalendarPage/CalendarParts/StartBooking.tsx +17 -0
  74. package/src/components/CalendarPage/CalendarParts/WeekDays.tsx +27 -0
  75. package/src/components/CalendarPage/FormCreator.tsx +213 -0
  76. package/src/components/CalendarPage/FormItems/{Date.js → Date.tsx} +10 -2
  77. package/src/components/CalendarPage/FormItems/{NumberSelect.js → NumberSelect.tsx} +1 -1
  78. package/src/components/CalendarPage/FormItems/{Select.js → Select.tsx} +0 -0
  79. package/src/components/CalendarPage/FormItems/{index.js → index.ts} +0 -0
  80. package/src/components/CalendarPage/PriceField/Price.tsx +58 -0
  81. package/src/components/CalendarPage/PriceField/Queries.ts +23 -0
  82. package/src/components/CalendarPage/PriceField/index.tsx +127 -0
  83. package/src/components/CalendarPage/Summary/{CostRow.js → CostRow.tsx} +19 -3
  84. package/src/components/CalendarPage/Summary/{CostSection.js → CostSection.tsx} +5 -1
  85. package/src/components/CalendarPage/Summary/{CostSummary.js → CostSummary.tsx} +19 -10
  86. package/src/components/CalendarPage/Summary/Description.tsx +27 -0
  87. package/src/components/CalendarPage/Summary/{InsurancesAndRequired.js → InsurancesAndRequired.tsx} +21 -2
  88. package/src/components/CalendarPage/Summary/Object.tsx +59 -0
  89. package/src/components/CalendarPage/Summary/{OnSite.js → OnSite.tsx} +9 -9
  90. package/src/components/CalendarPage/Summary/{OptionalNotOnSite.js → OptionalNotOnSite.tsx} +19 -18
  91. package/src/components/CalendarPage/Summary/{OptionalOnSite.js → OptionalOnSite.tsx} +6 -1
  92. package/src/components/CalendarPage/Summary/{Queries.js → Queries.ts} +3 -3
  93. package/src/components/CalendarPage/Summary/RentAndDiscount.tsx +30 -0
  94. package/src/components/CalendarPage/Summary/{Totals.js → Totals.tsx} +8 -3
  95. package/src/components/CalendarPage/Summary/cost_types.d.ts +31 -0
  96. package/src/components/CalendarPage/Summary/index.tsx +24 -0
  97. package/src/components/CalendarPage/calender_types.d.ts +16 -0
  98. package/src/components/CalendarPage/formParts/AssistanceMessage.tsx +60 -0
  99. package/src/components/CalendarPage/formParts/{BookingHelpers.js → BookingHelpers.tsx} +3 -3
  100. package/src/components/CalendarPage/formParts/{BookingOrOption.js → BookingOrOption.tsx} +6 -1
  101. package/src/components/CalendarPage/formParts/CancelInsuranceText.tsx +105 -0
  102. package/src/components/CalendarPage/formParts/{DefaultBookingFields.js → DefaultBookingFields.ts} +3 -1
  103. package/src/components/CalendarPage/formParts/DiscountCode.tsx +62 -0
  104. package/src/components/CalendarPage/formParts/{Guests.js → Guests.tsx} +10 -4
  105. package/src/components/CalendarPage/formParts/{OptionalBookingFields.js → OptionalBookingFields.tsx} +0 -0
  106. package/src/components/CalendarPage/formParts/{OptionalCosts.js → OptionalCosts.tsx} +1 -2
  107. package/src/components/CalendarPage/formParts/{SuccessMessage.js → SuccessMessage.tsx} +0 -0
  108. package/src/components/CalendarPage/formParts/Validations.tsx +78 -0
  109. package/src/components/CalendarPage/formParts/{discount.js → discount.tsx} +11 -2
  110. package/src/components/CalendarPage/formParts/form_types.d.ts +38 -0
  111. package/src/components/CalendarPage/formParts/{insurances.js → insurances.tsx} +14 -10
  112. package/src/components/CalendarPage/formParts/{radioButtons.js → radioButtons.tsx} +0 -0
  113. package/src/components/Error/{ApiError.js → ApiError.tsx} +6 -4
  114. package/src/components/Error/{IntegrationError.js → IntegrationError.tsx} +17 -11
  115. package/src/components/Error/{index.js → index.ts} +0 -0
  116. package/src/components/{ErrorBoundary.js → ErrorBoundary.tsx} +13 -5
  117. package/src/components/Modal/index.tsx +46 -0
  118. package/src/components/ReviewsPage/Queries.ts +26 -0
  119. package/src/components/ReviewsPage/ReviewsPage.tsx +43 -0
  120. package/src/components/ReviewsPage/Score.tsx +25 -0
  121. package/src/components/ReviewsPage/SingleReview.tsx +38 -0
  122. package/src/components/SafeBooking.tsx +97 -0
  123. package/src/components/SearchPage/Field.tsx +75 -0
  124. package/src/components/SearchPage/Filters.tsx +91 -0
  125. package/src/components/SearchPage/Paginator.tsx +63 -0
  126. package/src/components/SearchPage/Results.tsx +129 -0
  127. package/src/components/SearchPage/{SearchPage.js → SearchPage.tsx} +42 -31
  128. package/src/components/SearchPage/{SingleResult.js → SingleResult.tsx} +15 -8
  129. package/src/components/SearchPage/filters/Categories.tsx +57 -0
  130. package/src/components/SearchPage/filters/DateFilter.tsx +34 -0
  131. package/src/components/SearchPage/filters/List.tsx +80 -0
  132. package/src/components/SearchPage/filters/NumberFilter.tsx +37 -0
  133. package/src/components/SearchPage/filters/Radio.tsx +46 -0
  134. package/src/components/SearchPage/filters/Select.tsx +85 -0
  135. package/src/components/SearchPage/filters/__tests__/helper.spec.js +15 -0
  136. package/src/components/SearchPage/filters/filter_types.d.ts +25 -0
  137. package/src/components/SearchPage/filters/helper.ts +19 -0
  138. package/src/components/icons/ArrowLeft.svg.tsx +20 -0
  139. package/src/components/icons/{ArrowRight.svg.js → ArrowRight.svg.tsx} +0 -0
  140. package/src/components/icons/{Reload.svg.js → Reload.svg.tsx} +0 -0
  141. package/src/components/icons/{info.svg.js → info.svg.tsx} +0 -0
  142. package/src/components/icons/{loading.svg.js → loading.svg.tsx} +1 -1
  143. package/src/custom.d.ts +10 -0
  144. package/src/index.tsx +93 -0
  145. package/src/locales/de.json +4 -3
  146. package/src/locales/en.json +4 -3
  147. package/src/locales/es.json +4 -3
  148. package/src/locales/fr.json +4 -3
  149. package/src/locales/it.json +4 -3
  150. package/src/locales/nl.json +4 -3
  151. package/src/styles/main.css +2 -1
  152. package/src/styles/modal.css +1 -1
  153. package/src/styles/pagination.css +25 -23
  154. package/src/styles/result.css +33 -2
  155. package/src/styles/reviews.css +76 -0
  156. package/src/types.d.ts +85 -0
  157. package/tsconfig.json +17 -0
  158. package/vite.config.ts +31 -0
  159. package/build/index.html +0 -16
  160. package/build/index.js +0 -48528
  161. package/cypress.json +0 -9
  162. package/rollup.config.js +0 -30
  163. package/src/_lib/format.js +0 -16
  164. package/src/components/App.js +0 -164
  165. package/src/components/CalendarPage/BookingForm.js +0 -57
  166. package/src/components/CalendarPage/Calendar.js +0 -373
  167. package/src/components/CalendarPage/CalendarHeader.js +0 -58
  168. package/src/components/CalendarPage/CalendarPage.js +0 -158
  169. package/src/components/CalendarPage/FormCreator.js +0 -278
  170. package/src/components/CalendarPage/FormItems/Wrapper.js +0 -0
  171. package/src/components/CalendarPage/PriceField.js +0 -203
  172. package/src/components/CalendarPage/Summary/Description.js +0 -22
  173. package/src/components/CalendarPage/Summary/Object.js +0 -46
  174. package/src/components/CalendarPage/Summary/RentAndDiscount.js +0 -21
  175. package/src/components/CalendarPage/Summary/index.js +0 -19
  176. package/src/components/CalendarPage/formParts/AssistanceMessage.js +0 -47
  177. package/src/components/CalendarPage/formParts/CancelInsuranceText.js +0 -91
  178. package/src/components/CalendarPage/formParts/DiscountCode.js +0 -62
  179. package/src/components/CalendarPage/formParts/summary.js +0 -43
  180. package/src/components/Modal/index.js +0 -58
  181. package/src/components/ReviewsPage/ReviewsPage.js +0 -15
  182. package/src/components/SafeBooking.js +0 -69
  183. package/src/components/SearchPage/Field.js +0 -241
  184. package/src/components/SearchPage/Filters.js +0 -108
  185. package/src/components/SearchPage/Paginator.js +0 -59
  186. package/src/components/SearchPage/Results.js +0 -130
  187. package/src/components/SearchPage/filters/List.js +0 -63
  188. package/src/components/icons/ArrowLeft.svg.js +0 -18
  189. package/src/index.js +0 -74
  190. package/webpack.config.dev.js +0 -53
  191. package/webpack.config.js +0 -67
@@ -0,0 +1,27 @@
1
+ import { startOfWeek, addDays } from "date-fns";
2
+ import React from "react";
3
+ import { FormatIntl } from "../../../_lib/date_helper";
4
+
5
+ interface Props {
6
+ month: Date
7
+ }
8
+
9
+ function WeekDays({ month }: Props):JSX.Element {
10
+ const dateFormat = 'E';
11
+ let days: JSX.Element[] = [];
12
+
13
+ let startDate: Date = startOfWeek(month);
14
+
15
+ for (let i = 0; i < 7; i++) {
16
+ days.push(
17
+ <div className="col col-center" key={i}>
18
+ {FormatIntl(addDays(startDate, i), dateFormat)}
19
+ </div>
20
+ );
21
+ }
22
+
23
+ return <div className="days row">{days}</div>;
24
+
25
+ }
26
+
27
+ export default WeekDays
@@ -0,0 +1,213 @@
1
+ import React, { useContext } from 'react';
2
+ import { Formik, Form } from 'formik';
3
+ import { FormattedMessage } from 'react-intl';
4
+ import { CREATE_BOOKING_MUTATION } from '../../_lib/queries';
5
+ import { Insurances } from './formParts/insurances';
6
+ import Discount from './formParts/discount';
7
+ import Summary from './Summary';
8
+ import Modal from '../Modal';
9
+ import DefaultBookingFields from './formParts/DefaultBookingFields';
10
+ import SuccessMessage from './formParts/SuccessMessage';
11
+ import OptionalBookingFields from './formParts/OptionalBookingFields';
12
+ import { ApiError } from '../Error';
13
+ import { initializeBookingFields } from './formParts/BookingHelpers';
14
+ import OptionalCosts from './formParts/OptionalCosts';
15
+ import Guests from './formParts/Guests';
16
+ import { validateForm } from './formParts/Validations';
17
+ import { AppContext } from '../AppContext';
18
+ import { HouseType, PortalSiteType } from '../../types';
19
+ import { BookingType } from './calender_types';
20
+ import { useMutation } from '@apollo/client';
21
+ import {
22
+ CalendarContext,
23
+ CalendarContextDispatch
24
+ } from './CalendarParts/CalendarContext';
25
+
26
+ interface Props {
27
+ house: HouseType;
28
+ PortalSite: PortalSiteType;
29
+ booking: BookingType;
30
+ }
31
+
32
+ function FormCreator({ house, PortalSite }: Props): JSX.Element {
33
+ const { persons, arrivalDate, departureDate } = useContext(CalendarContext);
34
+ const { locale, portalCode, objectCode } = useContext(AppContext);
35
+ const dispatch = useContext(CalendarContextDispatch);
36
+
37
+ const { options } = PortalSite;
38
+
39
+ const bookingFields = options.bookingFields || DefaultBookingFields;
40
+
41
+ const bookingPrice = house.booking_price;
42
+
43
+ let costs: any = {};
44
+
45
+ for (const val of bookingPrice.optional_house_costs) {
46
+ costs[val.id] = '0';
47
+ }
48
+
49
+ const [createBooking, { loading, error, data }] = useMutation(
50
+ CREATE_BOOKING_MUTATION
51
+ );
52
+
53
+ if (data) {
54
+ return (
55
+ <Modal show={true}>
56
+ <SuccessMessage />
57
+ </Modal>
58
+ );
59
+ }
60
+
61
+ const optBookingFieldsInitialized = initializeBookingFields(bookingFields);
62
+
63
+ return (
64
+ <Formik
65
+ validate={(values) => validateForm(values, house, bookingFields)}
66
+ initialValues={{
67
+ ...optBookingFieldsInitialized,
68
+ arrivalDate,
69
+ departureDate,
70
+ is_option: 'false',
71
+ costs,
72
+ adults: persons,
73
+ children: 0,
74
+ babies: 0,
75
+ persons,
76
+ discount: 0,
77
+ country: 'nl'
78
+ }}
79
+ onSubmit={(values, { setSubmitting }) => {
80
+ let variables = {
81
+ ...values,
82
+ is_option: JSON.parse(values.is_option),
83
+ house_code: objectCode,
84
+ portal_code: portalCode,
85
+ comment: values.comment || '',
86
+ language: locale,
87
+ country: values.country.toUpperCase(),
88
+ adults: Number(values.adults),
89
+ children: Number(values.children) || 0,
90
+ babies: Number(values.babies) || 0,
91
+ discount: Number(values.discount) || 0,
92
+ cancel_insurance: Number(values.cancel_insurance) || 0,
93
+ arrival_date: values.arrivalDate.date,
94
+ departure_date: values.departureDate.date,
95
+ costs: JSON.stringify(values.costs),
96
+ extra_fields: JSON.stringify(values.extra_fields)
97
+ };
98
+
99
+ createBooking({ variables })
100
+ .then(() => {
101
+ if (
102
+ options.bookingForm &&
103
+ options.bookingForm.redirectUrl &&
104
+ options.bookingForm.redirectUrl !== ''
105
+ ) {
106
+ window.location = options.bookingForm.redirectUrl;
107
+ } else {
108
+ setTimeout(() => {
109
+ dispatch({
110
+ type: 'return'
111
+ });
112
+ }, 15000);
113
+ }
114
+ })
115
+ .catch((err) => {});
116
+ }}
117
+ >
118
+ {({ errors, touched, values, status, isSubmitting }) => (
119
+ <Form className="form">
120
+ {loading && <div className="return-message">Loading...</div>}
121
+ {error && (
122
+ <Modal show={true}>
123
+ <ApiError errors={error} modal={true} />
124
+ </Modal>
125
+ )}
126
+
127
+ <div className="form-content">
128
+ <div className="form-section">
129
+ <a
130
+ className="return-link"
131
+ role="link"
132
+ tabIndex={0}
133
+ onClick={() => {
134
+ dispatch({
135
+ type: 'return'
136
+ });
137
+ }}
138
+ >
139
+ <FormattedMessage id="return_to_calendar" />
140
+ </a>
141
+ <h2>
142
+ <FormattedMessage id="stay_details" />
143
+ </h2>
144
+ <Guests options={options} house={house} />
145
+
146
+ {errors.max_persons && (
147
+ <div className="error-message persons">
148
+ {errors.max_persons}
149
+ </div>
150
+ )}
151
+ </div>
152
+ <Discount
153
+ errors={errors}
154
+ house={house}
155
+ options={options}
156
+ values={values}
157
+ render={bookingPrice.optional_house_costs}
158
+ />
159
+
160
+ <Insurances house={house} values={values} />
161
+
162
+ <OptionalCosts costs={bookingPrice.optional_house_costs} />
163
+
164
+ <OptionalBookingFields
165
+ bookingFields={bookingFields}
166
+ errors={errors}
167
+ touched={touched}
168
+ PortalSite={PortalSite}
169
+ values={values}
170
+ />
171
+ </div>
172
+
173
+ <div className="form-sum">
174
+ <Summary house={house} values={values} />
175
+ {status && status.msg && <div>{status.msg}</div>}
176
+ <div className="terms">
177
+ <FormattedMessage id="agree_with" />{' '}
178
+ <FormattedMessage id="terms">
179
+ {(fm) => (
180
+ <Modal buttonText={fm}>
181
+ <div
182
+ style={{
183
+ width: '90vh',
184
+ height: '90vh'
185
+ }}
186
+ >
187
+ <iframe
188
+ src={house.rental_terms}
189
+ width="100%"
190
+ height="100%"
191
+ title="Terms"
192
+ />
193
+ </div>
194
+ </Modal>
195
+ )}
196
+ </FormattedMessage>
197
+ </div>
198
+ {[1, 2].includes(Number(values.cancel_insurance)) ? (
199
+ <div className="terms">
200
+ <FormattedMessage id="comply_insurance_card" />
201
+ </div>
202
+ ) : null}
203
+ <button className="button" type="submit" disabled={isSubmitting}>
204
+ <FormattedMessage id="book" />
205
+ </button>
206
+ </div>
207
+ </Form>
208
+ )}
209
+ </Formik>
210
+ );
211
+ }
212
+
213
+ export default FormCreator;
@@ -4,7 +4,15 @@ import { FormattedMessage } from 'react-intl';
4
4
  import DatePicker from 'react-date-picker';
5
5
  import { format } from 'date-fns';
6
6
 
7
- function DateField({ label, description, options, name, inline }) {
7
+ interface Props {
8
+ label: string
9
+ description: string | React.ReactNode
10
+ name: string
11
+ inline: boolean
12
+ required: boolean
13
+ }
14
+
15
+ function DateField({ label, description, name, inline }:Props) {
8
16
  return (
9
17
  <Field name={name}>
10
18
  {({ field, meta, form }) => {
@@ -47,7 +55,7 @@ function DateField({ label, description, options, name, inline }) {
47
55
  }
48
56
 
49
57
  DateField.defaultValues = {
50
- inline: true,
58
+ inline: true
51
59
  };
52
60
 
53
61
  export default DateField;
@@ -8,7 +8,7 @@ export default function NumberSelect({ label, description, count, ...props }) {
8
8
 
9
9
  return (
10
10
  <Field name={props.name}>
11
- {({ field, meta, form }) => {
11
+ {({ field, meta }) => {
12
12
  return (
13
13
  <div className="form-row inline" id={`bukazu_form_${props.name}`}>
14
14
  <label htmlFor={props.name}>
@@ -0,0 +1,58 @@
1
+ import { useQuery } from '@apollo/client';
2
+ import React, { useContext } from 'react';
3
+ import { FormattedMessage, FormattedNumber } from 'react-intl';
4
+ import { AppContext } from '../../AppContext';
5
+ import { ApiError } from '../../Error';
6
+ import Loading from '../../icons/loading.svg';
7
+ import { BOOKING_PRICE_QUERY } from './Queries';
8
+
9
+ interface Props {
10
+ persons: number;
11
+ variables: {
12
+ starts_at: string;
13
+ ends_at: string;
14
+ };
15
+ }
16
+
17
+ function Price({ persons, variables }: Props) {
18
+ const { portalCode, objectCode } = useContext(AppContext);
19
+ const { loading, error, data } = useQuery(BOOKING_PRICE_QUERY, {
20
+ variables: { ...variables, persons, portalCode, objectCode }
21
+ });
22
+
23
+ if (loading)
24
+ return (
25
+ <div className="price-overview--build">
26
+ <Loading />
27
+ </div>
28
+ );
29
+ if (error) {
30
+ return (
31
+ <div className="price-overview--build">
32
+ <ApiError errors={error}></ApiError>
33
+ </div>
34
+ );
35
+ }
36
+ const result = data.PortalSite.houses[0].booking_price;
37
+ return (
38
+ <>
39
+ <div className="price-overview--book">
40
+ <div className="price">
41
+ €{' '}
42
+ <FormattedNumber
43
+ value={Math.round(result.total_price)}
44
+ minimumFractionDigits={2}
45
+ maximumFractionDigits={2}
46
+ />
47
+ </div>
48
+ <div>
49
+ <i>
50
+ <FormattedMessage id="based_on_one_person" values={{ persons }} />
51
+ </i>
52
+ </div>
53
+ </div>
54
+ </>
55
+ );
56
+ }
57
+
58
+ export default Price;
@@ -0,0 +1,23 @@
1
+ import { gql } from "@apollo/client";
2
+
3
+ export const BOOKING_PRICE_QUERY = gql`
4
+ query BookingPriceQuery(
5
+ $portalCode: ID!
6
+ $objectCode: String!
7
+ $starts_at: Date!
8
+ $ends_at: Date!
9
+ $persons: Int
10
+ ) {
11
+ PortalSite(id: $portalCode) {
12
+ houses(house_code: $objectCode) {
13
+ id
14
+ name
15
+ booking_price(
16
+ starts_at: $starts_at
17
+ ends_at: $ends_at
18
+ persons: $persons
19
+ )
20
+ }
21
+ }
22
+ }
23
+ `;
@@ -0,0 +1,127 @@
1
+ import React, { useContext, useState } from 'react';
2
+ import {
3
+ FormatIntl,
4
+ LONG_DATE_FORMAT,
5
+ Parse_EN_US
6
+ } from '../../../_lib/date_helper';
7
+ import { FormattedMessage } from 'react-intl';
8
+ import { createPeronsArray } from '../formParts/BookingHelpers';
9
+ import Price from './Price';
10
+ import { HouseType } from '../../../types';
11
+ import {
12
+ CalendarContext,
13
+ CalendarContextDispatch
14
+ } from '../CalendarParts/CalendarContext';
15
+
16
+ const dateFormat = LONG_DATE_FORMAT;
17
+
18
+ interface Props {
19
+ house: HouseType;
20
+ }
21
+
22
+ function PriceField({ house }: Props) {
23
+ const [persons, setPersons] = useState(2);
24
+
25
+ const { arrivalDate, departureDate } = useContext(CalendarContext);
26
+ const dispatch = useContext(CalendarContextDispatch);
27
+
28
+ let adults = createPeronsArray(house.persons);
29
+
30
+ return (
31
+ <div className="calendar--picker">
32
+ <div className="calendar--picker--date">
33
+ <span className="name">
34
+ <FormattedMessage id={`${house.house_type}.arrival`} />
35
+ </span>
36
+ <span className="detail">
37
+ {arrivalDate?.date ? (
38
+ <span>
39
+ {FormatIntl(Parse_EN_US(arrivalDate?.date), dateFormat)}
40
+ </span>
41
+ ) : (
42
+ <FormattedMessage
43
+ id={`${house.house_type}.pick_your_arrivaldate_in_the_calendar`}
44
+ />
45
+ )}
46
+ </span>
47
+ </div>
48
+ <div className="calendar--picker--date">
49
+ <span className="name">
50
+ <FormattedMessage id={`${house.house_type}.departure`} />
51
+ </span>
52
+ <span className="detail">
53
+ {departureDate?.date ? (
54
+ <span>
55
+ {FormatIntl(Parse_EN_US(departureDate?.date), dateFormat)}
56
+ </span>
57
+ ) : (
58
+ <div>
59
+ <div>
60
+ <FormattedMessage
61
+ id={`${house.house_type}.pick_your_departure_in_the_calendar`}
62
+ />
63
+ </div>
64
+ {arrivalDate && (
65
+ <FormattedMessage
66
+ id="minimum_nights"
67
+ defaultMessage="Minimum {minimum} nights"
68
+ values={{ minimum: arrivalDate?.min_nights }}
69
+ />
70
+ )}
71
+ </div>
72
+ )}
73
+ </span>
74
+ </div>
75
+ <div className="calendar--picker--date">
76
+ <span className="detail">
77
+ <select
78
+ className="calendar--picker--persons"
79
+ value={persons}
80
+ onChange={(e) => {
81
+ setPersons(e.target.value);
82
+ }}
83
+ >
84
+ {adults.map((person) => (
85
+ <FormattedMessage
86
+ id="persons"
87
+ key={person}
88
+ children={(text) => (
89
+ <option value={person} key={person}>
90
+ {person} {text}
91
+ </option>
92
+ )}
93
+ />
94
+ ))}
95
+ </select>
96
+ </span>
97
+ </div>
98
+ <div className="calendar--picker--date">
99
+ {arrivalDate && departureDate && (
100
+ <Price
101
+ persons={parseInt(persons)}
102
+ variables={{
103
+ starts_at: arrivalDate?.date,
104
+ ends_at: departureDate?.date
105
+ }}
106
+ />
107
+ )}
108
+ </div>
109
+ <button
110
+ className="button"
111
+ disabled={!arrivalDate || !departureDate}
112
+ onClick={() => {
113
+ if (arrivalDate && departureDate) {
114
+ dispatch({
115
+ type: 'start',
116
+ persons
117
+ });
118
+ }
119
+ }}
120
+ >
121
+ <FormattedMessage id="calculate" />
122
+ </button>
123
+ </div>
124
+ );
125
+ }
126
+
127
+ export default PriceField;
@@ -2,7 +2,23 @@ import React from 'react';
2
2
  import { FormattedMessage, FormattedNumber } from 'react-intl';
3
3
  import Description from './Description';
4
4
 
5
- function CostRow({ name, amount, description, method_name, formatName, forceMethod }) {
5
+ interface Props {
6
+ name: string;
7
+ amount: number;
8
+ description?: string | React.ReactNode;
9
+ method_name?: string;
10
+ formatName?: boolean;
11
+ forceMethod?: boolean;
12
+ }
13
+
14
+ function CostRow({
15
+ name,
16
+ amount,
17
+ description,
18
+ method_name,
19
+ formatName,
20
+ forceMethod
21
+ }: Props): JSX.Element {
6
22
  return (
7
23
  <tr>
8
24
  <td>
@@ -24,7 +40,7 @@ function CostRow({ name, amount, description, method_name, formatName, forceMeth
24
40
  minimumFractionDigits={2}
25
41
  maximumFractionDigits={2}
26
42
  />
27
- {forceMethod && (<>{' '}{method_name}</>)}
43
+ {forceMethod && <> {method_name}</>}
28
44
  </>
29
45
  ) : (
30
46
  <>{method_name}</>
@@ -36,7 +52,7 @@ function CostRow({ name, amount, description, method_name, formatName, forceMeth
36
52
 
37
53
  CostRow.defaultValues = {
38
54
  formatName: false,
39
- forceMethod: false,
55
+ forceMethod: false
40
56
  };
41
57
 
42
58
  export default CostRow;
@@ -1,6 +1,10 @@
1
1
  import React from 'react';
2
2
 
3
- export default function CostSection({ children }) {
3
+ interface Props {
4
+ children: React.ReactNode;
5
+ }
6
+
7
+ export default function CostSection({ children }: Props): JSX.Element {
4
8
  return (
5
9
  <div className="costs-section">
6
10
  <table>
@@ -1,32 +1,41 @@
1
- import React from 'react';
1
+ import React, { useContext } from 'react';
2
2
  import { useQuery } from '@apollo/client';
3
- import InsurancesAndRequired from './InsurancesAndRequired';
3
+ import InsurancesAndRequired from './InsurancesAndRequired.tsx';
4
4
  import { BOOKING_PRICE_TOTAL_QUERY } from './Queries';
5
5
  import RentAndDiscount from './RentAndDiscount';
6
6
  import OptionalNotOnSite from './OptionalNotOnSite';
7
7
  import OnSite from './OnSite';
8
8
  import Totals from './Totals';
9
+ import { AppContext } from '../../AppContext';
10
+ import { HouseType } from '../../../types';
11
+ import { PossibleValues } from '../formParts/form_types';
9
12
 
10
- function CostSummary({ values, house }) {
13
+ interface Props {
14
+ values: PossibleValues;
15
+ house: HouseType;
16
+ }
17
+
18
+ function CostSummary({ values, house }: Props): React.ReactNode {
11
19
  let babies = Number(values.babies) - Number(house.babies_extra);
12
- if (babies < 0) {
13
- babies = 0;
14
- }
20
+ if (babies < 0) {
21
+ babies = 0;
22
+ }
15
23
  const persons = Number(values.children) + Number(values.adults) + babies;
24
+ const { portalCode, objectCode } = useContext(AppContext);
16
25
 
17
26
  const { loading, error, data } = useQuery(BOOKING_PRICE_TOTAL_QUERY, {
18
27
  variables: {
19
- id: values.portalCode,
28
+ id: portalCode,
20
29
  persons: persons,
21
- house_id: values.objectCode,
30
+ house_id: objectCode,
22
31
  starts_at: JSON.stringify(values.arrivalDate.date),
23
32
  ends_at: JSON.stringify(values.departureDate.date),
24
33
  costs: JSON.stringify(values.costs),
25
34
  discount: Number(values.discount),
26
35
  discount_code: values.discount_code,
27
- cancel_insurance: Number(values.cancel_insurance),
36
+ cancel_insurance: Number(values.cancel_insurance)
28
37
  },
29
- fetchPolicy: 'network-only',
38
+ fetchPolicy: 'network-only'
30
39
  });
31
40
 
32
41
  if (loading) {
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import Icon from '../../icons/info.svg';
3
+ import Modal from '../../Modal';
4
+
5
+ interface Props {
6
+ description: string | React.ReactNode;
7
+ }
8
+
9
+ function Description({ description }: Props): JSX.Element {
10
+ let val = <span />;
11
+ if (description) {
12
+ val = (
13
+ <span
14
+ style={{
15
+ padding: '0 0 0 8px'
16
+ }}
17
+ >
18
+ <Modal buttonText={<Icon />}>
19
+ <p>{description}</p>
20
+ </Modal>
21
+ </span>
22
+ );
23
+ }
24
+ return val;
25
+ }
26
+
27
+ export default Description;
@@ -1,7 +1,24 @@
1
1
  import React from 'react';
2
2
  import CostRow from './CostRow';
3
+ import { CostType } from './cost_types';
3
4
 
4
- export default function InsurancesAndRequired({ prices }) {
5
+ interface Props {
6
+ prices: {
7
+ total_costs: {
8
+ insurances: {
9
+ cancel_insurance: number;
10
+ };
11
+ required_costs: {
12
+ not_on_site: CostType[];
13
+ };
14
+ };
15
+ required_house_costs: CostType[];
16
+ };
17
+ }
18
+
19
+ export default function InsurancesAndRequired({
20
+ prices
21
+ }: Props): React.ReactNode {
5
22
  const { insurances, required_costs } = prices.total_costs;
6
23
  const { not_on_site } = required_costs;
7
24
  return (
@@ -25,7 +42,9 @@ export default function InsurancesAndRequired({ prices }) {
25
42
  if (cost.method === 'none') {
26
43
  return <CostRow key={cost.id} {...cost} />;
27
44
  } else {
28
- if (cost.amount === 0) { return null; }
45
+ if (cost.amount === 0) {
46
+ return null;
47
+ }
29
48
  return (
30
49
  <CostRow
31
50
  key={cost.id}