@qite/tide-booking-component 0.0.2-preview.9 → 1.0.0-preview

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 (143) hide show
  1. package/README.md +1 -0
  2. package/build/build-cjs/booking-wizard/components/message.d.ts +1 -0
  3. package/build/build-cjs/booking-wizard/components/product-card.d.ts +1 -2
  4. package/build/build-cjs/booking-wizard/features/booking/api.d.ts +21 -0
  5. package/build/build-cjs/booking-wizard/features/booking/booking-slice.d.ts +95 -19
  6. package/build/build-cjs/booking-wizard/features/booking/booking.d.ts +1 -2
  7. package/build/build-cjs/booking-wizard/features/booking/selectors.d.ts +208 -0
  8. package/build/build-cjs/booking-wizard/features/price-details/price-details-api.d.ts +8 -6
  9. package/build/build-cjs/booking-wizard/features/price-details/price-details-slice.d.ts +98 -30
  10. package/build/build-cjs/booking-wizard/features/price-details/util.d.ts +5 -0
  11. package/build/build-cjs/booking-wizard/features/product-options/no-options.d.ts +2 -0
  12. package/build/build-cjs/booking-wizard/features/product-options/none-option.d.ts +17 -0
  13. package/build/build-cjs/booking-wizard/features/product-options/option-booking-group.d.ts +18 -0
  14. package/build/build-cjs/booking-wizard/features/product-options/option-item.d.ts +11 -0
  15. package/build/build-cjs/booking-wizard/features/product-options/option-pax-card.d.ts +9 -0
  16. package/build/build-cjs/booking-wizard/features/product-options/option-pax-group.d.ts +20 -0
  17. package/build/build-cjs/booking-wizard/features/product-options/option-room.d.ts +16 -0
  18. package/build/build-cjs/booking-wizard/features/product-options/option-unit-group.d.ts +20 -0
  19. package/build/build-cjs/booking-wizard/features/product-options/option-units-card.d.ts +9 -0
  20. package/build/build-cjs/booking-wizard/features/sidebar/index.d.ts +5 -1
  21. package/build/build-cjs/booking-wizard/features/sidebar/sidebar-flight.d.ts +8 -0
  22. package/build/build-cjs/booking-wizard/features/sidebar/sidebar-util.d.ts +22 -5
  23. package/build/build-cjs/booking-wizard/features/sidebar/sidebar.d.ts +16 -8
  24. package/build/build-cjs/booking-wizard/features/summary/summary-booking-option-pax.d.ts +7 -0
  25. package/build/build-cjs/booking-wizard/features/summary/summary-booking-option-unit.d.ts +7 -0
  26. package/build/build-cjs/booking-wizard/features/summary/summary-flight.d.ts +8 -0
  27. package/build/build-cjs/booking-wizard/features/summary/summary-per-booking-option-group.d.ts +10 -0
  28. package/build/build-cjs/booking-wizard/features/summary/summary-per-pax-option-group.d.ts +10 -0
  29. package/build/build-cjs/booking-wizard/features/summary/summary-per-unit-option-group.d.ts +10 -0
  30. package/build/build-cjs/booking-wizard/features/summary/summary-slice.d.ts +0 -28
  31. package/build/build-cjs/booking-wizard/features/travelers-form/travelers-form-slice.d.ts +82 -88
  32. package/build/build-cjs/booking-wizard/features/travelers-form/type-ahead-input.d.ts +1 -0
  33. package/build/build-cjs/booking-wizard/features/travelers-form/validate-form.d.ts +2 -1
  34. package/build/build-cjs/booking-wizard/index.d.ts +3 -2
  35. package/build/build-cjs/booking-wizard/store.d.ts +23 -45
  36. package/build/build-cjs/booking-wizard/types.d.ts +63 -11
  37. package/build/build-cjs/booking-wizard/utils/query-string-util.d.ts +13 -0
  38. package/build/build-cjs/index.js +12065 -2381
  39. package/build/build-esm/booking-wizard/components/message.d.ts +1 -0
  40. package/build/build-esm/booking-wizard/components/product-card.d.ts +1 -2
  41. package/build/build-esm/booking-wizard/features/booking/api.d.ts +21 -0
  42. package/build/build-esm/booking-wizard/features/booking/booking-slice.d.ts +95 -19
  43. package/build/build-esm/booking-wizard/features/booking/booking.d.ts +1 -2
  44. package/build/build-esm/booking-wizard/features/booking/selectors.d.ts +208 -0
  45. package/build/build-esm/booking-wizard/features/price-details/price-details-api.d.ts +8 -6
  46. package/build/build-esm/booking-wizard/features/price-details/price-details-slice.d.ts +98 -30
  47. package/build/build-esm/booking-wizard/features/price-details/util.d.ts +5 -0
  48. package/build/build-esm/booking-wizard/features/product-options/no-options.d.ts +2 -0
  49. package/build/build-esm/booking-wizard/features/product-options/none-option.d.ts +17 -0
  50. package/build/build-esm/booking-wizard/features/product-options/option-booking-group.d.ts +18 -0
  51. package/build/build-esm/booking-wizard/features/product-options/option-item.d.ts +11 -0
  52. package/build/build-esm/booking-wizard/features/product-options/option-pax-card.d.ts +9 -0
  53. package/build/build-esm/booking-wizard/features/product-options/option-pax-group.d.ts +20 -0
  54. package/build/build-esm/booking-wizard/features/product-options/option-room.d.ts +16 -0
  55. package/build/build-esm/booking-wizard/features/product-options/option-unit-group.d.ts +20 -0
  56. package/build/build-esm/booking-wizard/features/product-options/option-units-card.d.ts +9 -0
  57. package/build/build-esm/booking-wizard/features/sidebar/index.d.ts +5 -1
  58. package/build/build-esm/booking-wizard/features/sidebar/sidebar-flight.d.ts +8 -0
  59. package/build/build-esm/booking-wizard/features/sidebar/sidebar-util.d.ts +22 -5
  60. package/build/build-esm/booking-wizard/features/sidebar/sidebar.d.ts +16 -8
  61. package/build/build-esm/booking-wizard/features/summary/summary-booking-option-pax.d.ts +7 -0
  62. package/build/build-esm/booking-wizard/features/summary/summary-booking-option-unit.d.ts +7 -0
  63. package/build/build-esm/booking-wizard/features/summary/summary-flight.d.ts +8 -0
  64. package/build/build-esm/booking-wizard/features/summary/summary-per-booking-option-group.d.ts +10 -0
  65. package/build/build-esm/booking-wizard/features/summary/summary-per-pax-option-group.d.ts +10 -0
  66. package/build/build-esm/booking-wizard/features/summary/summary-per-unit-option-group.d.ts +10 -0
  67. package/build/build-esm/booking-wizard/features/summary/summary-slice.d.ts +0 -28
  68. package/build/build-esm/booking-wizard/features/travelers-form/travelers-form-slice.d.ts +82 -88
  69. package/build/build-esm/booking-wizard/features/travelers-form/type-ahead-input.d.ts +1 -0
  70. package/build/build-esm/booking-wizard/features/travelers-form/validate-form.d.ts +2 -1
  71. package/build/build-esm/booking-wizard/index.d.ts +3 -2
  72. package/build/build-esm/booking-wizard/store.d.ts +23 -45
  73. package/build/build-esm/booking-wizard/types.d.ts +63 -11
  74. package/build/build-esm/booking-wizard/utils/query-string-util.d.ts +13 -0
  75. package/build/build-esm/index.js +11888 -2330
  76. package/package.json +8 -4
  77. package/rollup.config.js +1 -6
  78. package/src/booking-wizard/components/message.tsx +14 -8
  79. package/src/booking-wizard/components/product-card.tsx +10 -39
  80. package/src/booking-wizard/components/step-indicator.tsx +1 -1
  81. package/src/booking-wizard/components/step-route.tsx +1 -1
  82. package/src/booking-wizard/features/booking/api.ts +44 -0
  83. package/src/booking-wizard/features/booking/booking-slice.ts +274 -40
  84. package/src/booking-wizard/features/booking/booking.tsx +193 -57
  85. package/src/booking-wizard/features/booking/selectors.ts +328 -0
  86. package/src/booking-wizard/features/confirmation/confirmation.tsx +22 -11
  87. package/src/booking-wizard/features/error/error.tsx +15 -2
  88. package/src/booking-wizard/features/price-details/price-details-api.ts +12 -6
  89. package/src/booking-wizard/features/price-details/price-details-slice.ts +80 -50
  90. package/src/booking-wizard/features/price-details/util.ts +135 -0
  91. package/src/booking-wizard/features/product-options/no-options.tsx +18 -0
  92. package/src/booking-wizard/features/product-options/none-option.tsx +118 -0
  93. package/src/booking-wizard/features/product-options/option-booking-group.tsx +210 -0
  94. package/src/booking-wizard/features/product-options/option-item.tsx +321 -0
  95. package/src/booking-wizard/features/product-options/option-pax-card.tsx +102 -0
  96. package/src/booking-wizard/features/product-options/option-pax-group.tsx +169 -0
  97. package/src/booking-wizard/features/product-options/option-room.tsx +300 -0
  98. package/src/booking-wizard/features/product-options/option-unit-group.tsx +192 -0
  99. package/src/booking-wizard/features/product-options/option-units-card.tsx +100 -0
  100. package/src/booking-wizard/features/product-options/options-form.tsx +226 -48
  101. package/src/booking-wizard/features/sidebar/index.tsx +43 -20
  102. package/src/booking-wizard/features/sidebar/sidebar-flight.tsx +66 -0
  103. package/src/booking-wizard/features/sidebar/sidebar-util.ts +81 -39
  104. package/src/booking-wizard/features/sidebar/sidebar.tsx +150 -100
  105. package/src/booking-wizard/features/summary/summary-booking-option-pax.tsx +25 -0
  106. package/src/booking-wizard/features/summary/summary-booking-option-unit.tsx +25 -0
  107. package/src/booking-wizard/features/summary/summary-flight.tsx +35 -0
  108. package/src/booking-wizard/features/summary/summary-per-booking-option-group.tsx +57 -0
  109. package/src/booking-wizard/features/summary/summary-per-pax-option-group.tsx +51 -0
  110. package/src/booking-wizard/features/summary/summary-per-unit-option-group.tsx +54 -0
  111. package/src/booking-wizard/features/summary/summary-slice.ts +1 -134
  112. package/src/booking-wizard/features/summary/summary.tsx +521 -134
  113. package/src/booking-wizard/features/travelers-form/travelers-form-slice.ts +54 -55
  114. package/src/booking-wizard/features/travelers-form/travelers-form.tsx +202 -94
  115. package/src/booking-wizard/features/travelers-form/type-ahead-input.tsx +23 -3
  116. package/src/booking-wizard/features/travelers-form/validate-form.ts +40 -3
  117. package/src/booking-wizard/index.tsx +5 -6
  118. package/src/booking-wizard/settings-context.ts +33 -5
  119. package/src/booking-wizard/store.ts +5 -4
  120. package/src/booking-wizard/translations/translations.json +95 -61
  121. package/src/booking-wizard/types.ts +67 -10
  122. package/src/booking-wizard/utils/query-string-util.ts +42 -0
  123. package/build/build-cjs/booking-wizard/features/product-options/checkbox.d.ts +0 -11
  124. package/build/build-cjs/booking-wizard/features/product-options/option-details.d.ts +0 -11
  125. package/build/build-cjs/booking-wizard/features/product-options/product-options-api.d.ts +0 -29
  126. package/build/build-cjs/booking-wizard/features/product-options/product-options-slice.d.ts +0 -75
  127. package/build/build-cjs/booking-wizard/features/product-options/radio-button.d.ts +0 -10
  128. package/build/build-cjs/booking-wizard/features/product-options/rooms-slice.d.ts +0 -23
  129. package/build/build-cjs/booking-wizard/features/product-options/tree-level.d.ts +0 -7
  130. package/build/build-esm/booking-wizard/features/product-options/checkbox.d.ts +0 -11
  131. package/build/build-esm/booking-wizard/features/product-options/option-details.d.ts +0 -11
  132. package/build/build-esm/booking-wizard/features/product-options/product-options-api.d.ts +0 -29
  133. package/build/build-esm/booking-wizard/features/product-options/product-options-slice.d.ts +0 -75
  134. package/build/build-esm/booking-wizard/features/product-options/radio-button.d.ts +0 -10
  135. package/build/build-esm/booking-wizard/features/product-options/rooms-slice.d.ts +0 -23
  136. package/build/build-esm/booking-wizard/features/product-options/tree-level.d.ts +0 -7
  137. package/src/booking-wizard/features/product-options/checkbox.tsx +0 -38
  138. package/src/booking-wizard/features/product-options/option-details.tsx +0 -65
  139. package/src/booking-wizard/features/product-options/product-options-api.ts +0 -148
  140. package/src/booking-wizard/features/product-options/product-options-slice.ts +0 -149
  141. package/src/booking-wizard/features/product-options/radio-button.tsx +0 -35
  142. package/src/booking-wizard/features/product-options/rooms-slice.ts +0 -28
  143. package/src/booking-wizard/features/product-options/tree-level.tsx +0 -118
@@ -1,28 +1,27 @@
1
1
  import { PayloadAction, createSelector, createSlice } from "@reduxjs/toolkit";
2
- import { differenceInYears, parse } from "date-fns";
2
+ import { differenceInYears } from "date-fns";
3
3
 
4
4
  import { RootState } from "../../store";
5
5
  import { TravelersFormValues } from "../../types";
6
6
 
7
7
  export interface TravelersFormState {
8
- adultCount: number;
9
- childCount: number;
10
8
  formValues?: TravelersFormValues;
11
9
  }
12
10
 
11
+ export const CHILD_MAX_AGE = 17;
13
12
  const isDebug = false;
14
13
 
15
14
  const debugFormValues = {
16
15
  adults: [
17
16
  {
18
- id: "1",
17
+ id: 1,
19
18
  firstName: "Jan",
20
19
  lastName: "Jansens",
21
20
  birthDate: "1984-05-20",
22
21
  gender: "m",
23
22
  },
24
23
  {
25
- id: "2",
24
+ id: 2,
26
25
  firstName: "Leen",
27
26
  lastName: "Leenders",
28
27
  birthDate: "1986-08-09",
@@ -46,21 +45,21 @@ const debugFormValues = {
46
45
  // },
47
46
  // ],
48
47
  children: [],
49
- mainBookerId: "1",
48
+ mainBookerId: 1,
50
49
  street: "Nieuwstraat",
51
50
  houseNumber: "20",
52
51
  box: "A",
53
52
  zipCode: "9000",
54
53
  place: "Gent",
55
54
  country: "be",
56
- email: "jan.jansens@fake.be",
57
- emailConfirmation: "jan.jansens@fake.be",
55
+ email: "staging@qite.be",
56
+ emailConfirmation: "staging@qite.be",
58
57
  phone: "+32 471 12 34 56",
58
+ travelAgentId: 0,
59
+ travelAgentName: "",
59
60
  };
60
61
 
61
62
  const initialState: TravelersFormState = {
62
- adultCount: 2,
63
- childCount: 0,
64
63
  formValues: isDebug ? debugFormValues : undefined,
65
64
  };
66
65
 
@@ -68,25 +67,13 @@ const travelersFormSlice = createSlice({
68
67
  name: "travelersForm",
69
68
  initialState,
70
69
  reducers: {
71
- setAdultCount(state, action: PayloadAction<number>) {
72
- state.adultCount = action.payload;
73
- },
74
- setChildCount(state, action: PayloadAction<number>) {
75
- state.childCount = action.payload;
76
- },
77
70
  setFormValues(state, action: PayloadAction<TravelersFormValues>) {
78
71
  state.formValues = action.payload;
79
72
  },
80
73
  },
81
74
  });
82
75
 
83
- export const { setAdultCount, setChildCount, setFormValues } =
84
- travelersFormSlice.actions;
85
-
86
- export const selectAdultCount = (state: RootState) =>
87
- state.travelersForm.adultCount;
88
- export const selectChildCount = (state: RootState) =>
89
- state.travelersForm.childCount;
76
+ export const { setFormValues } = travelersFormSlice.actions;
90
77
 
91
78
  export const selectTravelersFormValues = (state: RootState) =>
92
79
  state.travelersForm.formValues;
@@ -101,40 +88,52 @@ export const selectChildren = createSelector(
101
88
  (formValues) => formValues?.children ?? []
102
89
  );
103
90
 
104
- export const selectTravelerAges = createSelector(
105
- selectAdultCount,
106
- selectChildCount,
91
+ export const selectAgentId = createSelector(
107
92
  selectTravelersFormValues,
108
- (adultCount, childCount, travelerFormValues) => {
109
- if (!travelerFormValues) {
110
- return [
111
- ...new Array(adultCount).fill(30),
112
- ...new Array(childCount).fill(6),
113
- ];
114
- }
115
-
116
- return [
117
- ...(travelerFormValues.adults ?? []).map((adult) => {
118
- const diff = differenceInYears(
119
- new Date(),
120
- parse(adult.birthDate, "yyyy-MM-dd", new Date())
121
- );
122
- return isNaN(diff) ? 30 : diff;
123
- }),
124
- ...(travelerFormValues.children ?? []).map((traveler) => {
125
- const diff = differenceInYears(
126
- new Date(),
127
- parse(traveler.birthDate, "yyyy-MM-dd", new Date())
128
- );
129
- return isNaN(diff) ? 6 : diff;
130
- }),
131
- ];
132
- }
93
+ (formValues) => formValues?.travelAgentId
133
94
  );
134
95
 
135
- export const selectMainBookerId = createSelector(
136
- selectTravelersFormValues,
137
- (formValues) => formValues?.mainBookerId
138
- );
96
+ const selectRequestRooms = (state: RootState) =>
97
+ state.booking.package?.options.find((x) => x.isSelected)?.requestRooms;
98
+
99
+ export const selectAdultIds = createSelector(selectRequestRooms, (rooms) => {
100
+ const ids: number[] = [];
101
+ if (rooms) {
102
+ rooms.forEach((x) =>
103
+ x.pax.forEach((p) => {
104
+ if (p.age && p.age > CHILD_MAX_AGE) {
105
+ ids.push(p.id);
106
+ } else if (p.dateOfBirth) {
107
+ const diff = differenceInYears(new Date(), new Date(p.dateOfBirth));
108
+
109
+ if (diff > CHILD_MAX_AGE) {
110
+ ids.push(p.id);
111
+ }
112
+ }
113
+ })
114
+ );
115
+ }
116
+ return ids;
117
+ });
118
+
119
+ export const selectChildIds = createSelector(selectRequestRooms, (rooms) => {
120
+ const ids: number[] = [];
121
+ if (rooms) {
122
+ rooms.forEach((x) =>
123
+ x.pax.forEach((p) => {
124
+ if (p.age && p.age <= CHILD_MAX_AGE) {
125
+ ids.push(p.id);
126
+ } else if (p.dateOfBirth) {
127
+ const diff = differenceInYears(new Date(), new Date(p.dateOfBirth));
128
+
129
+ if (diff <= CHILD_MAX_AGE) {
130
+ ids.push(p.id);
131
+ }
132
+ }
133
+ })
134
+ );
135
+ }
136
+ return ids;
137
+ });
139
138
 
140
139
  export default travelersFormSlice.reducer;
@@ -1,14 +1,12 @@
1
- import React, { useContext } from "react";
2
- import { compact, get, range } from "lodash";
1
+ import React, { useContext, useEffect, useState } from "react";
2
+ import { compact, get, sortBy } from "lodash";
3
3
  import {
4
- selectAdultCount,
5
- selectChildCount,
4
+ selectAdultIds,
5
+ selectChildIds,
6
6
  selectTravelersFormValues,
7
- setAdultCount,
8
- setChildCount,
9
7
  setFormValues,
10
8
  } from "./travelers-form-slice";
11
- import { useDispatch, useSelector } from "react-redux";
9
+ import { useSelector } from "react-redux";
12
10
 
13
11
  import LabeledInput from "../../components/labeled-input";
14
12
  import LabeledSelect from "../../components/labeled-select";
@@ -16,27 +14,40 @@ import SettingsContext from "../../settings-context";
16
14
  import { TravelersFormValues } from "../../types";
17
15
  import { buildClassName } from "../../utils/class-util";
18
16
  import { fetchPriceDetails } from "../price-details/price-details-slice";
19
- import { fetchProductOptions } from "../product-options/product-options-slice";
20
17
  import flat from "flat";
21
18
  import { navigate } from "@reach/router";
22
19
  import produce from "immer";
23
- import { selectBookingQueryString } from "../booking/booking-slice";
20
+ import {
21
+ selectAgentAdressId,
22
+ selectAgents,
23
+ selectBookingQueryString,
24
+ selectStartDate,
25
+ } from "../booking/selectors";
24
26
  import translations from "../../translations/translations.json";
25
27
  import { useFormik } from "formik";
26
- import { v4 as uuid } from "uuid";
27
28
  import validateForm from "./validate-form";
29
+ import { format, parse } from "date-fns";
30
+ import { useAppDispatch } from "../../store";
31
+ import TypeAheadInput from "./type-ahead-input";
32
+ import { setBookingType } from "../booking/booking-slice";
28
33
 
29
34
  interface TravelersFormProps {}
30
35
 
31
- function createTraveler() {
32
- return { id: uuid(), firstName: "", lastName: "", birthDate: "", gender: "" };
36
+ function createTraveler(id: number) {
37
+ return { id, firstName: "", lastName: "", birthDate: "", gender: "" };
33
38
  }
34
39
 
35
- function createInitialValues(adultCount = 2, childCount = 0) {
40
+ function createInitialValues(
41
+ adultIds: number[],
42
+ childIds: number[],
43
+ startDate?: string,
44
+ agentAdressId?: number
45
+ ) {
36
46
  const initialValues = {
37
- adults: range(0, adultCount).map(() => createTraveler()),
38
- children: range(0, childCount).map(() => createTraveler()),
39
- mainBookerId: "",
47
+ startDate: startDate,
48
+ adults: adultIds.map((id) => createTraveler(id)),
49
+ children: childIds.map((id) => createTraveler(id)),
50
+ mainBookerId: -1,
40
51
  street: "",
41
52
  houseNumber: "",
42
53
  box: "",
@@ -46,52 +57,76 @@ function createInitialValues(adultCount = 2, childCount = 0) {
46
57
  phone: "",
47
58
  email: "",
48
59
  emailConfirmation: "",
60
+ travelAgentId: agentAdressId ?? 0,
61
+ travelAgentName: "",
49
62
  };
50
63
 
51
- initialValues.mainBookerId = initialValues.adults[0].id;
64
+ if (initialValues.adults && initialValues.adults.length) {
65
+ initialValues.mainBookerId = initialValues.adults[0].id;
66
+ }
52
67
 
53
68
  return initialValues;
54
69
  }
55
70
 
56
71
  const TravelersForm: React.FC<TravelersFormProps> = () => {
57
- const dispatch = useDispatch();
72
+ const dispatch = useAppDispatch();
58
73
 
59
74
  const settings = useContext(SettingsContext);
60
75
  const bookingQueryString = useSelector(selectBookingQueryString);
61
- const adultCount = useSelector(selectAdultCount);
62
- const childCount = useSelector(selectChildCount);
76
+ const startDate = useSelector(selectStartDate);
77
+ const adultIds = useSelector(selectAdultIds);
78
+ const childIds = useSelector(selectChildIds);
79
+ const agents = useSelector(selectAgents);
80
+ const agentAdressId = useSelector(selectAgentAdressId);
81
+
63
82
  const initialValues =
64
83
  useSelector(selectTravelersFormValues) ??
65
- createInitialValues(adultCount, childCount);
84
+ createInitialValues(adultIds, childIds, startDate, agentAdressId);
85
+
86
+ const [showAgents, setShowAgents] = useState<boolean>(
87
+ settings.agentRequired ?? false
88
+ );
89
+ const [showAgentSelection, setShowAgentSelection] = useState<boolean>(
90
+ !settings.agentAdressId && !settings.hideAgentSelection
91
+ );
92
+
93
+ const typeaheadAgents =
94
+ sortBy(
95
+ agents?.map((x) => ({
96
+ key: `${x.id}`,
97
+ value: `${x.name} (${x.postalCode} ${x.location})`,
98
+ text: `${x.name} (${x.postalCode} ${x.location})`,
99
+ })),
100
+ "value"
101
+ ) ?? [];
102
+
103
+ const [filteredAgents, setFilteredAgents] =
104
+ useState<{ key: string; value: string; text: string }[]>(typeaheadAgents);
66
105
 
67
106
  const formik = useFormik<TravelersFormValues>({
68
107
  initialValues,
69
- validate: validateForm,
108
+ validate: (values) => validateForm(values, settings.agentRequired),
70
109
  onSubmit: (values) => {
71
110
  dispatch(setFormValues(values));
72
111
  dispatch(fetchPriceDetails());
73
112
 
74
113
  navigate(
75
- `${settings.basePath}${settings.optionsPathSuffix}?${bookingQueryString}`
114
+ `${settings.basePath}${settings.options.pathSuffix}?${bookingQueryString}`
76
115
  );
77
116
  },
78
117
  });
79
118
 
80
- const handleAdultCountChange: React.FormEventHandler<HTMLSelectElement> = (
81
- e
82
- ) => {
83
- const count = parseInt(e.currentTarget.value);
84
- dispatch(setAdultCount(count));
85
-
119
+ useEffect(() => {
86
120
  const formValues = produce(formik.values, (values) => {
87
- values.adults = range(0, count).map(
88
- (i) => values.adults[i] ?? createTraveler()
121
+ values.adults = adultIds.map(
122
+ (id) => values.adults[id] ?? createTraveler(id)
89
123
  );
90
124
 
91
125
  if (
92
126
  values.adults.findIndex(
93
127
  (traveler) => traveler.id === values.mainBookerId
94
- ) === -1
128
+ ) === -1 &&
129
+ values.adults.length
95
130
  ) {
96
131
  values.mainBookerId = values.adults[0].id;
97
132
  }
@@ -99,31 +134,89 @@ const TravelersForm: React.FC<TravelersFormProps> = () => {
99
134
 
100
135
  formik.setValues(formValues, false);
101
136
  dispatch(setFormValues(formValues));
137
+ }, [adultIds]);
102
138
 
103
- dispatch(fetchProductOptions());
104
- };
105
-
106
- const handleChildCountChange: React.FormEventHandler<HTMLSelectElement> = (
107
- e
108
- ) => {
109
- const count = parseInt(e.currentTarget.value);
110
- dispatch(setChildCount(count));
111
-
139
+ useEffect(() => {
112
140
  const formValues = produce(formik.values, (values) => {
113
- values.children = range(0, count).map(
114
- (i) => values.children[i] ?? createTraveler()
141
+ values.children = childIds.map(
142
+ (id) => values.children[id] ?? createTraveler(id)
115
143
  );
116
144
  });
117
145
  formik.setValues(formValues, false);
118
146
  dispatch(setFormValues(formValues));
147
+ }, [childIds]);
119
148
 
120
- dispatch(fetchProductOptions());
149
+ useEffect(() => {
150
+ dispatch(fetchPriceDetails());
151
+ }, []);
152
+
153
+ useEffect(() => {
154
+ if (agents && settings.affiliateSlug) {
155
+ const agent = agents.find(
156
+ (x) => x.affiliateSlug && x.affiliateSlug === settings.affiliateSlug
157
+ );
158
+ if (!agent) return;
159
+
160
+ const formValues = produce(formik.values, (values) => {
161
+ (values.travelAgentId = Number(agent.id)),
162
+ (values.travelAgentName = agent.name);
163
+ });
164
+ formik.setValues(formValues, false);
165
+ dispatch(setFormValues(formValues));
166
+ setShowAgentSelection(false);
167
+ }
168
+ }, [agents, settings.affiliateSlug]);
169
+
170
+ const handleMainBookerChange: React.FormEventHandler<HTMLInputElement> = (
171
+ e
172
+ ) => {
173
+ const id = parseInt(e.currentTarget.value);
174
+
175
+ formik.setFieldValue("mainBookerId", id);
121
176
  };
122
177
 
123
178
  const mainBooker = formik.values.adults.find(
124
179
  (traveler) => traveler.id === formik.values.mainBookerId
125
180
  );
126
181
 
182
+ const handleAgentChange = (value: string) => {
183
+ const filteredAgents = typeaheadAgents.filter(
184
+ (x) => x.value.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1
185
+ );
186
+
187
+ setFilteredAgents(filteredAgents);
188
+ formik.setFieldValue("travelAgentName", value);
189
+ };
190
+
191
+ const handleAgentSelect = (key: string) => {
192
+ const agent = typeaheadAgents.find((x) => x.key === key);
193
+
194
+ formik.setFieldValue("travelAgentId", Number(agent?.key));
195
+ formik.setFieldValue("travelAgentName", agent?.value);
196
+
197
+ let bookingType = "b2b2c";
198
+ if (agentAdressId && agentAdressId != 0) {
199
+ bookingType = "b2b";
200
+ }
201
+ dispatch(setBookingType(bookingType));
202
+ };
203
+
204
+ const handleAgentClear = () => {
205
+ formik.setFieldValue("travelAgentId", 0);
206
+ formik.setFieldValue("travelAgentName", "");
207
+
208
+ dispatch(setBookingType("b2c"));
209
+ };
210
+
211
+ const toggleAgent = (value: boolean) => {
212
+ setShowAgents(value);
213
+
214
+ if (!value) {
215
+ handleAgentClear();
216
+ setFilteredAgents([]);
217
+ }
218
+ };
219
+
127
220
  const flatErrors: Record<string, string> = flat(formik.errors);
128
221
  const hasVisibleError = (key: string) =>
129
222
  get(formik.errors, key) && get(formik.touched, key);
@@ -131,6 +224,8 @@ const TravelersForm: React.FC<TravelersFormProps> = () => {
131
224
  return (
132
225
  <form
133
226
  className="form"
227
+ name="booking--step1"
228
+ id="booking--step1"
134
229
  noValidate
135
230
  onSubmit={formik.handleSubmit}
136
231
  onReset={formik.handleReset}
@@ -140,44 +235,21 @@ const TravelersForm: React.FC<TravelersFormProps> = () => {
140
235
  <h5 className="form__region-heading">
141
236
  {translations.TRAVELERS_FORM.PERSONS}
142
237
  </h5>
143
- </div>
144
- <div className="form__row">
145
- <label
146
- className={buildClassName(["form__group", "form__group--sm-50"])}
147
- >
148
- <span className="form__label">
149
- {translations.TRAVELERS_FORM.TRAVELER_COUNT} *
150
- </span>
151
- <div className="dropdown">
152
- <select onChange={handleAdultCountChange} value={adultCount}>
153
- {range(1, 11).map((i) => (
154
- <option key={i} value={i}>{`${i} ${
155
- i === 1
156
- ? translations.TRAVELERS_FORM.ADULT
157
- : translations.TRAVELERS_FORM.ADULTS
158
- }`}</option>
159
- ))}
160
- </select>
161
- </div>
162
- </label>
163
- <label
164
- className={buildClassName(["form__group", "form__group--sm-50"])}
165
- >
166
- <span className="form__label">
167
- {translations.TRAVELERS_FORM.CHILDREN} *
168
- </span>
169
- <div className="dropdown">
170
- <select onChange={handleChildCountChange} value={childCount}>
171
- {range(0, 11).map((i) => (
172
- <option key={i} value={i}>{`${i} ${
173
- i === 1
174
- ? translations.TRAVELERS_FORM.CHILD
175
- : translations.TRAVELERS_FORM.CHILDREN
176
- }`}</option>
177
- ))}
178
- </select>
179
- </div>
180
- </label>
238
+ <p className="form__region-label">
239
+ {compact([
240
+ adultIds.length,
241
+ adultIds.length === 1 && ` ${translations.TRAVELERS_FORM.ADULT}`,
242
+ adultIds.length > 1 && ` ${translations.TRAVELERS_FORM.ADULTS}`,
243
+ adultIds &&
244
+ adultIds.length &&
245
+ childIds &&
246
+ childIds.length &&
247
+ ", ",
248
+ childIds.length,
249
+ childIds.length === 1 && ` ${translations.TRAVELERS_FORM.CHILD}`,
250
+ childIds.length > 1 && ` ${translations.TRAVELERS_FORM.CHILDREN}`,
251
+ ]).join("")}
252
+ </p>
181
253
  </div>
182
254
  </div>
183
255
  {formik.values.adults.map((travelerValues, index) => (
@@ -195,7 +267,7 @@ const TravelersForm: React.FC<TravelersFormProps> = () => {
195
267
  <input
196
268
  type="radio"
197
269
  name="mainBookerId"
198
- onChange={formik.handleChange}
270
+ onChange={handleMainBookerChange}
199
271
  onBlur={formik.handleBlur}
200
272
  value={travelerValues.id}
201
273
  checked={formik.values.mainBookerId === travelerValues.id}
@@ -412,7 +484,11 @@ const TravelersForm: React.FC<TravelersFormProps> = () => {
412
484
  <p className="form__region-label">
413
485
  {compact([
414
486
  compact([mainBooker?.firstName, mainBooker?.lastName]).join(" "),
415
- mainBooker?.birthDate,
487
+ mainBooker?.birthDate &&
488
+ format(
489
+ parse(mainBooker.birthDate, "yyyy-MM-dd", new Date()),
490
+ "dd-MM-yyyy"
491
+ ),
416
492
  ]).join(", ")}
417
493
  </p>
418
494
  </div>
@@ -428,7 +504,7 @@ const TravelersForm: React.FC<TravelersFormProps> = () => {
428
504
  name="street"
429
505
  onChange={formik.handleChange}
430
506
  onBlur={formik.handleBlur}
431
- placeholder="Jouw adres"
507
+ placeholder={translations.TRAVELERS_FORM.STREET_PLACEHOLDER}
432
508
  value={formik.values.street}
433
509
  />
434
510
  <LabeledInput
@@ -471,7 +547,7 @@ const TravelersForm: React.FC<TravelersFormProps> = () => {
471
547
  label={translations.TRAVELERS_FORM.CITY}
472
548
  required
473
549
  name="place"
474
- placeholder="Plaats"
550
+ placeholder={translations.TRAVELERS_FORM.CITY_PLACEHOLDER}
475
551
  onChange={formik.handleChange}
476
552
  onBlur={formik.handleBlur}
477
553
  value={formik.values.place}
@@ -546,16 +622,48 @@ const TravelersForm: React.FC<TravelersFormProps> = () => {
546
622
  />
547
623
  </div>
548
624
  </div>
549
- {/* <div className="form__region">
550
- <div className="form__region-header">
551
- <h5 className="form__region-heading">Ik wens te boeken bij:</h5>
552
- </div>
553
- <div className="form__row">
554
- <div className="form__group form__group--icon">
555
- <TypeAheadInput value="" options={[]} name="agent" placeholder="Kies uw reisagent" />
625
+ {showAgentSelection && (
626
+ <div className="form__region">
627
+ <div className="form__region-header">
628
+ <h5 className="form__region-heading">
629
+ Ik wens te boeken bij mijn lokale reisagent
630
+ </h5>
631
+ <div className="checkbox" id="cbxChooseOffice">
632
+ <label className="checkbox__label">
633
+ <input
634
+ type="checkbox"
635
+ name="booking--mainbooker"
636
+ defaultChecked={showAgents}
637
+ onClick={() => toggleAgent(!showAgents)}
638
+ className="checkbox__input"
639
+ />
640
+ Ik kies een kantoor
641
+ </label>
642
+ </div>
556
643
  </div>
644
+ {showAgents && (
645
+ <div className="form__row form__row--choose-office">
646
+ <div
647
+ className={buildClassName([
648
+ "form__group",
649
+ "form__group--icon",
650
+ hasVisibleError("travelAgentId") && "form__group--error",
651
+ ])}
652
+ >
653
+ <TypeAheadInput
654
+ value={formik.values.travelAgentName}
655
+ options={filteredAgents}
656
+ onChange={handleAgentChange}
657
+ onSelect={handleAgentSelect}
658
+ onClear={handleAgentClear}
659
+ name="travelAgentName"
660
+ placeholder="Kies uw reisagent"
661
+ />
662
+ </div>
663
+ </div>
664
+ )}
557
665
  </div>
558
- </div> */}
666
+ )}
559
667
  {Object.keys(flatErrors).length > 0 && (
560
668
  <div className="form__region form__region--errors">
561
669
  <div className="form__row">
@@ -1,5 +1,5 @@
1
1
  import Icon from "../../components/icon";
2
- import React from "react";
2
+ import React, { useState } from "react";
3
3
 
4
4
  interface TypeAheadInputProps {
5
5
  name?: string;
@@ -7,6 +7,7 @@ interface TypeAheadInputProps {
7
7
  placeholder?: string;
8
8
  options?: { key: string; value: string | number; text: string }[];
9
9
  onChange?: (value: string) => void;
10
+ onSelect?: (key: string) => void;
10
11
  onClear?: () => void;
11
12
  }
12
13
 
@@ -16,8 +17,11 @@ const TypeAheadInput: React.FC<TypeAheadInputProps> = ({
16
17
  placeholder,
17
18
  options,
18
19
  onChange,
20
+ onSelect,
19
21
  onClear,
20
22
  }) => {
23
+ const [showItems, setShowItems] = useState<boolean>(false);
24
+
21
25
  const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
22
26
  if (onChange) {
23
27
  onChange(e.target.value);
@@ -28,6 +32,7 @@ const TypeAheadInput: React.FC<TypeAheadInputProps> = ({
28
32
  if (onClear) {
29
33
  onClear();
30
34
  }
35
+ setShowItems(false);
31
36
  };
32
37
 
33
38
  return (
@@ -42,6 +47,14 @@ const TypeAheadInput: React.FC<TypeAheadInputProps> = ({
42
47
  onChange={handleChange}
43
48
  placeholder={placeholder}
44
49
  required
50
+ autoComplete="off"
51
+ onKeyDown={(e) => {
52
+ if (e.key === "Tab" && options && onSelect) {
53
+ onSelect(options[0].key);
54
+ setShowItems(false);
55
+ }
56
+ }}
57
+ onFocus={() => setShowItems(true)}
45
58
  />
46
59
  <button
47
60
  type="button"
@@ -49,7 +62,7 @@ const TypeAheadInput: React.FC<TypeAheadInputProps> = ({
49
62
  onClick={handleClearClick}
50
63
  ></button>
51
64
  </div>
52
- {options && options.length > 0 && (
65
+ {options && options.length > 0 && showItems && (
53
66
  <div className="typeahead__options">
54
67
  {(options ?? []).map((option) => {
55
68
  const matchIndex =
@@ -61,7 +74,14 @@ const TypeAheadInput: React.FC<TypeAheadInputProps> = ({
61
74
  return (
62
75
  <div className="typeahead__option" key={option.key}>
63
76
  <div className="typeahead__option-header">
64
- <button type="button" className="button button--plain">
77
+ <button
78
+ onClick={() => {
79
+ if (onSelect) onSelect(option.key);
80
+ setShowItems(false);
81
+ }}
82
+ type="button"
83
+ className="button button--plain"
84
+ >
65
85
  {prefix}
66
86
  <span className="typeahead__option-highlight">
67
87
  {highlight}