mautourco-components 0.2.86 → 0.2.88
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/dist/components/molecules/VehicleSupplement/VehicleSupplement.js +3 -0
- package/dist/components/organisms/Booking/index.d.ts +1 -1
- package/dist/components/organisms/RoundTrip/RoundTrip.d.ts +12 -12
- package/dist/components/organisms/RoundTrip/RoundTrip.js +70 -27
- package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.js +8 -12
- package/dist/components/organisms/SearchBarTransfer/index.d.ts +1 -1
- package/dist/components/organisms/TransferLine/TransferLine.d.ts +10 -10
- package/dist/components/organisms/TransferLine/TransferLine.js +74 -35
- package/package.json +1 -1
- package/src/components/molecules/VehicleSupplement/VehicleSupplement.tsx +3 -0
- package/src/components/organisms/Booking/index.ts +5 -1
- package/src/components/organisms/RoundTrip/RoundTrip.tsx +90 -41
- package/src/components/organisms/SearchBarTransfer/SearchBarTransfer.tsx +4 -4
- package/src/components/organisms/SearchBarTransfer/index.ts +1 -1
- package/src/components/organisms/TransferLine/TransferLine.tsx +92 -47
|
@@ -51,6 +51,9 @@ var VehicleSupplement = function (_a) {
|
|
|
51
51
|
};
|
|
52
52
|
var getTransferTypeIcon = function (transferType) {
|
|
53
53
|
var type = transferType.toLowerCase();
|
|
54
|
+
if (type.includes('harbour')) {
|
|
55
|
+
return 'ship';
|
|
56
|
+
}
|
|
54
57
|
if (type.includes('arrival') || type.includes('airport') || type.includes('inbound')) {
|
|
55
58
|
return 'plane-landing-outline';
|
|
56
59
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export * from './Booking';
|
|
2
|
-
export type { BookingDocketProps } from './BookingDocket/BookingDocket';
|
|
2
|
+
export type { BookingDocketItem, BookingDocketProps, BookingResumeData, } from './BookingDocket/BookingDocket';
|
|
3
3
|
export type { BookingStepProps } from './BookingStep/BookingStep';
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import "../../../styles/components/organism/round-trip.css";
|
|
3
|
-
import {
|
|
3
|
+
import { LocationGroup, LocationOption } from "../../molecules/LocationDropdown/LocationDropdown";
|
|
4
4
|
import { PaxData } from "../PaxSelector/PaxSelector";
|
|
5
|
-
export type {
|
|
5
|
+
export type { LocationOption };
|
|
6
6
|
export interface RoundTripTransfer {
|
|
7
7
|
type: "arrival" | "departure";
|
|
8
8
|
paxData?: PaxData;
|
|
9
9
|
transferDate?: string;
|
|
10
|
-
pickupPoint?:
|
|
11
|
-
dropoffPoint?:
|
|
10
|
+
pickupPoint?: LocationOption;
|
|
11
|
+
dropoffPoint?: LocationOption;
|
|
12
12
|
}
|
|
13
13
|
export interface RoundTripData {
|
|
14
14
|
id: string;
|
|
15
15
|
paxData?: PaxData;
|
|
16
16
|
arrivalDate?: string;
|
|
17
17
|
departureDate?: string;
|
|
18
|
-
pickupDropoffPoint?:
|
|
19
|
-
accommodation?:
|
|
18
|
+
pickupDropoffPoint?: LocationOption;
|
|
19
|
+
accommodation?: LocationOption;
|
|
20
20
|
transfers: [RoundTripTransfer, RoundTripTransfer];
|
|
21
21
|
}
|
|
22
22
|
export interface RoundTripProps {
|
|
@@ -28,10 +28,10 @@ export interface RoundTripProps {
|
|
|
28
28
|
arrivalDate?: string;
|
|
29
29
|
/** Departure date */
|
|
30
30
|
departureDate?: string;
|
|
31
|
-
/** Selected pickup/dropoff point ID
|
|
32
|
-
pickupDropoffPoint?: string;
|
|
33
|
-
/** Selected accommodation ID
|
|
34
|
-
accommodation?: string;
|
|
31
|
+
/** Selected pickup/dropoff point - can be an ID string or LocationOption object */
|
|
32
|
+
pickupDropoffPoint?: string | LocationOption;
|
|
33
|
+
/** Selected accommodation - can be an ID string or LocationOption object */
|
|
34
|
+
accommodation?: string | LocationOption;
|
|
35
35
|
/** All location options */
|
|
36
36
|
locations?: {
|
|
37
37
|
options?: LocationOption[];
|
|
@@ -42,9 +42,9 @@ export interface RoundTripProps {
|
|
|
42
42
|
/** Callback when dates change */
|
|
43
43
|
onDatesChange?: (arrivalDate: string, departureDate: string) => void;
|
|
44
44
|
/** Callback when pickup/dropoff point changes */
|
|
45
|
-
onPickupDropoffChange?: (location:
|
|
45
|
+
onPickupDropoffChange?: (location: LocationOption | null) => void;
|
|
46
46
|
/** Callback when accommodation changes */
|
|
47
|
-
onAccommodationChange?: (location:
|
|
47
|
+
onAccommodationChange?: (location: LocationOption | null) => void;
|
|
48
48
|
/** Callback when any data changes - returns array of 2 transfers */
|
|
49
49
|
onChange?: (data: RoundTripData) => void;
|
|
50
50
|
/** Additional CSS classes */
|
|
@@ -75,43 +75,53 @@ var RoundTrip = function (_a) {
|
|
|
75
75
|
groups: accommodationGroups,
|
|
76
76
|
};
|
|
77
77
|
};
|
|
78
|
-
//
|
|
78
|
+
// Helper to check if value is LocationOption
|
|
79
|
+
var isLocationOption = function (value) {
|
|
80
|
+
return typeof value === 'object' && value !== null && 'id' in value && 'label' in value && 'type' in value;
|
|
81
|
+
};
|
|
82
|
+
// Initialize location data from IDs or LocationOption objects
|
|
79
83
|
useEffect(function () {
|
|
84
|
+
// If pickupDropoffPoint is already a LocationOption object, use it directly
|
|
85
|
+
if (isLocationOption(pickupDropoffPoint)) {
|
|
86
|
+
// Only update if the ID is different to prevent infinite loops
|
|
87
|
+
if ((internalPickupDropoffPoint === null || internalPickupDropoffPoint === void 0 ? void 0 : internalPickupDropoffPoint.id) !== pickupDropoffPoint.id) {
|
|
88
|
+
setInternalPickupDropoffPoint(pickupDropoffPoint);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
80
92
|
var _a = getPickupDropoffOptions(), options = _a.options, groups = _a.groups;
|
|
81
93
|
var allLocations = getAllLocations(options, groups);
|
|
82
94
|
if (pickupDropoffPoint) {
|
|
83
95
|
var location_1 = allLocations.find(function (loc) { return loc.id === pickupDropoffPoint; });
|
|
84
|
-
if (location_1) {
|
|
85
|
-
setInternalPickupDropoffPoint(
|
|
86
|
-
id: location_1.id,
|
|
87
|
-
locationName: location_1.label,
|
|
88
|
-
});
|
|
96
|
+
if (location_1 && (internalPickupDropoffPoint === null || internalPickupDropoffPoint === void 0 ? void 0 : internalPickupDropoffPoint.id) !== location_1.id) {
|
|
97
|
+
setInternalPickupDropoffPoint(location_1);
|
|
89
98
|
}
|
|
90
99
|
}
|
|
91
|
-
else {
|
|
92
|
-
// Set default to first airport if available
|
|
100
|
+
else if (!internalPickupDropoffPoint) {
|
|
101
|
+
// Set default to first airport if available (only if not already set)
|
|
93
102
|
var defaultAirport = allLocations.find(function (loc) { return loc.type === "airport"; });
|
|
94
103
|
if (defaultAirport) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
locationName: defaultAirport.label,
|
|
98
|
-
};
|
|
99
|
-
setInternalPickupDropoffPoint(defaultData);
|
|
100
|
-
onPickupDropoffChange === null || onPickupDropoffChange === void 0 ? void 0 : onPickupDropoffChange(defaultData);
|
|
104
|
+
setInternalPickupDropoffPoint(defaultAirport);
|
|
105
|
+
onPickupDropoffChange === null || onPickupDropoffChange === void 0 ? void 0 : onPickupDropoffChange(defaultAirport);
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
104
109
|
}, [pickupDropoffPoint, locations]);
|
|
105
110
|
useEffect(function () {
|
|
111
|
+
// If accommodation is already a LocationOption object, use it directly
|
|
112
|
+
if (isLocationOption(accommodation)) {
|
|
113
|
+
// Only update if the ID is different to prevent infinite loops
|
|
114
|
+
if ((internalAccommodation === null || internalAccommodation === void 0 ? void 0 : internalAccommodation.id) !== accommodation.id) {
|
|
115
|
+
setInternalAccommodation(accommodation);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
106
119
|
var _a = getAccommodationOptions(), options = _a.options, groups = _a.groups;
|
|
107
120
|
var allLocations = getAllLocations(options, groups);
|
|
108
121
|
if (accommodation) {
|
|
109
122
|
var location_2 = allLocations.find(function (loc) { return loc.id === accommodation; });
|
|
110
|
-
if (location_2) {
|
|
111
|
-
setInternalAccommodation(
|
|
112
|
-
id: location_2.id,
|
|
113
|
-
locationName: location_2.label,
|
|
114
|
-
});
|
|
123
|
+
if (location_2 && (internalAccommodation === null || internalAccommodation === void 0 ? void 0 : internalAccommodation.id) !== location_2.id) {
|
|
124
|
+
setInternalAccommodation(location_2);
|
|
115
125
|
}
|
|
116
126
|
}
|
|
117
127
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -164,13 +174,41 @@ var RoundTrip = function (_a) {
|
|
|
164
174
|
onDatesChange === null || onDatesChange === void 0 ? void 0 : onDatesChange(dates[0], dates[1]);
|
|
165
175
|
}
|
|
166
176
|
};
|
|
177
|
+
// Helper to convert LocationData to LocationOption
|
|
178
|
+
var toLocationOption = function (locationData, locationType) {
|
|
179
|
+
if (!locationData)
|
|
180
|
+
return null;
|
|
181
|
+
// Try to find in the options to get the type
|
|
182
|
+
var _a = locationType === 'pickup-dropoff' ? getPickupDropoffOptions() : getAccommodationOptions(), options = _a.options, groups = _a.groups;
|
|
183
|
+
var allLocations = getAllLocations(options, groups);
|
|
184
|
+
var found = allLocations.find(function (loc) { return loc.id === locationData.id; });
|
|
185
|
+
if (found)
|
|
186
|
+
return found;
|
|
187
|
+
// If not found, infer the type
|
|
188
|
+
var name = locationData.locationName.toLowerCase();
|
|
189
|
+
var id = String(locationData.id).toLowerCase();
|
|
190
|
+
var type = 'accommodation';
|
|
191
|
+
if (name.includes('harbour') || name.includes('port') || id.includes('harbour') || id.includes('port')) {
|
|
192
|
+
type = 'port';
|
|
193
|
+
}
|
|
194
|
+
else if (name.includes('airport') || id.includes('airport')) {
|
|
195
|
+
type = 'airport';
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
id: locationData.id,
|
|
199
|
+
label: locationData.locationName,
|
|
200
|
+
type: type,
|
|
201
|
+
};
|
|
202
|
+
};
|
|
167
203
|
var handlePickupDropoffChange = function (location) {
|
|
168
|
-
|
|
169
|
-
|
|
204
|
+
var locationOption = toLocationOption(location, 'pickup-dropoff');
|
|
205
|
+
setInternalPickupDropoffPoint(locationOption);
|
|
206
|
+
onPickupDropoffChange === null || onPickupDropoffChange === void 0 ? void 0 : onPickupDropoffChange(locationOption);
|
|
170
207
|
};
|
|
171
208
|
var handleAccommodationChange = function (location) {
|
|
172
|
-
|
|
173
|
-
|
|
209
|
+
var locationOption = toLocationOption(location, 'accommodation');
|
|
210
|
+
setInternalAccommodation(locationOption);
|
|
211
|
+
onAccommodationChange === null || onAccommodationChange === void 0 ? void 0 : onAccommodationChange(locationOption);
|
|
174
212
|
};
|
|
175
213
|
// Check if inputs are empty (when checkEmpty is true)
|
|
176
214
|
var isPaxEmpty = checkEmpty && (!internalPaxData ||
|
|
@@ -184,13 +222,18 @@ var RoundTrip = function (_a) {
|
|
|
184
222
|
var isAccommodationEmpty = checkEmpty && !internalAccommodation;
|
|
185
223
|
var pickupDropoffOptions = getPickupDropoffOptions();
|
|
186
224
|
var accommodationOptions = getAccommodationOptions();
|
|
187
|
-
// Helper function to find LocationOption from ID for defaultValue
|
|
188
|
-
var findLocationOption = function (
|
|
189
|
-
if (!
|
|
225
|
+
// Helper function to find LocationOption from ID or LocationOption for defaultValue
|
|
226
|
+
var findLocationOption = function (value, locationType) {
|
|
227
|
+
if (!value)
|
|
190
228
|
return undefined;
|
|
229
|
+
// If value is already a LocationOption object, use it directly
|
|
230
|
+
if (isLocationOption(value)) {
|
|
231
|
+
return value;
|
|
232
|
+
}
|
|
233
|
+
// Otherwise, look up by ID in the locations
|
|
191
234
|
var _a = locationType === 'pickup-dropoff' ? getPickupDropoffOptions() : getAccommodationOptions(), options = _a.options, groups = _a.groups;
|
|
192
235
|
var allLocations = getAllLocations(options, groups);
|
|
193
|
-
return allLocations.find(function (loc) { return loc.id ===
|
|
236
|
+
return allLocations.find(function (loc) { return loc.id === value; });
|
|
194
237
|
};
|
|
195
238
|
var defaultPickupDropoffOption = findLocationOption(pickupDropoffPoint, 'pickup-dropoff');
|
|
196
239
|
var defaultAccommodationOption = findLocationOption(accommodation, 'accommodation');
|
|
@@ -31,8 +31,7 @@ import RoundTrip from "../RoundTrip/RoundTrip";
|
|
|
31
31
|
import TransferLine from "../TransferLine/TransferLine";
|
|
32
32
|
var transferIdCounter = 0;
|
|
33
33
|
var SearchBarTransfer = function (_a) {
|
|
34
|
-
var _b, _c;
|
|
35
|
-
var _d = _a.mode, initialMode = _d === void 0 ? "roundtrip" : _d, _e = _a.locations, locations = _e === void 0 ? { options: [], groups: [] } : _e, defaultRoundTripData = _a.defaultRoundTripData, defaultTransferLines = _a.defaultTransferLines, _f = _a.defaultSameVehicle, defaultSameVehicle = _f === void 0 ? false : _f, onSearch = _a.onSearch, onChange = _a.onChange, onRemove = _a.onRemove, _g = _a.className, className = _g === void 0 ? "" : _g, _h = _a.scrollOnOpen, scrollOnOpen = _h === void 0 ? false : _h;
|
|
34
|
+
var _b = _a.mode, initialMode = _b === void 0 ? "roundtrip" : _b, _c = _a.locations, locations = _c === void 0 ? { options: [], groups: [] } : _c, defaultRoundTripData = _a.defaultRoundTripData, defaultTransferLines = _a.defaultTransferLines, _d = _a.defaultSameVehicle, defaultSameVehicle = _d === void 0 ? false : _d, onSearch = _a.onSearch, onChange = _a.onChange, onRemove = _a.onRemove, _e = _a.className, className = _e === void 0 ? "" : _e, _f = _a.scrollOnOpen, scrollOnOpen = _f === void 0 ? false : _f;
|
|
36
35
|
// Generate unique ID for transfer lines
|
|
37
36
|
var generateTransferId = function () {
|
|
38
37
|
transferIdCounter += 1;
|
|
@@ -44,11 +43,11 @@ var SearchBarTransfer = function (_a) {
|
|
|
44
43
|
return [];
|
|
45
44
|
return lines.map(function (line) { return (__assign(__assign({}, line), { id: line.id || generateTransferId() })); });
|
|
46
45
|
};
|
|
47
|
-
var
|
|
48
|
-
var
|
|
49
|
-
var
|
|
50
|
-
var
|
|
51
|
-
var
|
|
46
|
+
var _g = useState(initialMode), mode = _g[0], setMode = _g[1];
|
|
47
|
+
var _h = useState(defaultSameVehicle), sameVehicle = _h[0], setSameVehicle = _h[1];
|
|
48
|
+
var _j = useState(defaultRoundTripData), roundTripData = _j[0], setRoundTripData = _j[1];
|
|
49
|
+
var _k = useState(initializeTransferLines(defaultTransferLines)), transferLines = _k[0], setTransferLines = _k[1];
|
|
50
|
+
var _l = useState(null), error = _l[0], setError = _l[1];
|
|
52
51
|
// Notify parent of changes
|
|
53
52
|
useEffect(function () {
|
|
54
53
|
var data = {
|
|
@@ -310,7 +309,7 @@ var SearchBarTransfer = function (_a) {
|
|
|
310
309
|
onSearch === null || onSearch === void 0 ? void 0 : onSearch(data);
|
|
311
310
|
}
|
|
312
311
|
};
|
|
313
|
-
return (_jsxs("div", { className: "search-bar-transfer ".concat(className), children: [_jsxs("div", { className: "search-bar-transfer__header", children: [_jsx(Heading, { level: 5, variant: "bold", color: "accent", children: "Select your transfer details" }), _jsx("div", { className: "search-bar-transfer__mode-selector", children: _jsx(SegmentedButton, { options: modeOptions, value: mode, onChange: handleModeChange, name: "transfer-mode" }) })] }), mode === "roundtrip" ? (_jsxs(_Fragment, { children: [_jsx(RoundTrip, { id: "roundtrip-main", locations: locations, paxData: roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.paxData, arrivalDate: roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.arrivalDate, departureDate: roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.departureDate, pickupDropoffPoint:
|
|
312
|
+
return (_jsxs("div", { className: "search-bar-transfer ".concat(className), children: [_jsxs("div", { className: "search-bar-transfer__header", children: [_jsx(Heading, { level: 5, variant: "bold", color: "accent", children: "Select your transfer details" }), _jsx("div", { className: "search-bar-transfer__mode-selector", children: _jsx(SegmentedButton, { options: modeOptions, value: mode, onChange: handleModeChange, name: "transfer-mode" }) })] }), mode === "roundtrip" ? (_jsxs(_Fragment, { children: [_jsx(RoundTrip, { id: "roundtrip-main", locations: locations, paxData: roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.paxData, arrivalDate: roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.arrivalDate, departureDate: roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.departureDate, pickupDropoffPoint: roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.pickupDropoffPoint, accommodation: roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.accommodation, onChange: setRoundTripData, checkEmpty: error !== null, scrollOnOpen: scrollOnOpen }), _jsx("div", { className: "search-bar-transfer__transfer-type", children: renderTransferTypeButtons(handleAddTransferFromRoundTrip) })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "search-bar-transfer__transfer-type", children: [_jsx(Text, { size: "lg", variant: "bold", color: "subtle", as: "div", className: "search-bar-transfer__transfer-type-label", children: "Select a transfer type you want to add" }), renderTransferTypeButtons(handleAddTransfer)] }), transferLines.length === 0 && (_jsx(TransferLine, { id: "placeholder-transfer", type: "inter-hotel", locations: locations, onDataChange: function () { }, showDelete: false, disabled: true, scrollOnOpen: scrollOnOpen })), transferLines.length > 0 && (_jsx("div", { className: "search-bar-transfer__transfer-lines", children: (function () {
|
|
314
313
|
// Group transfers by type
|
|
315
314
|
var groupedTransfers = {
|
|
316
315
|
arrival: [],
|
|
@@ -329,10 +328,7 @@ var SearchBarTransfer = function (_a) {
|
|
|
329
328
|
departure: { label: "Departure", icon: "departure" },
|
|
330
329
|
"inter-hotel": { label: "Inter-Hotel", icon: "building" },
|
|
331
330
|
};
|
|
332
|
-
return nonEmptyGroups.map(function (type, groupIndex) { return (_jsxs(React.Fragment, { children: [_jsxs("div", { className: "search-bar-transfer__category-header", children: [_jsx(Icon, { name: categoryInfo[type].icon, size: "md" }), _jsx(Text, { size: "md", variant: "bold", color: "default", as: "span", children: categoryInfo[type].label })] }), groupedTransfers[type].map(function (line) {
|
|
333
|
-
var _a, _b;
|
|
334
|
-
return (_jsx(TransferLine, { id: line.id, type: line.type, paxData: line.paxData, transferDate: line.transferDate, pickupPoint: (_a = line.pickupPoint) === null || _a === void 0 ? void 0 : _a.id, dropoffPoint: (_b = line.dropoffPoint) === null || _b === void 0 ? void 0 : _b.id, locations: locations, onDataChange: function (data) { return handleTransferLineChange(line.id, data); }, onDelete: function () { return handleDeleteTransferLine(line.id); }, showDelete: transferLines.length > 1, checkEmpty: error !== null, scrollOnOpen: scrollOnOpen }, line.id));
|
|
335
|
-
}), groupIndex < nonEmptyGroups.length - 1 && (_jsx("svg", { className: "search-bar-transfer__divider", xmlns: "http://www.w3.org/2000/svg", width: "100%", height: "1", viewBox: "0 0 1076 1", fill: "none", preserveAspectRatio: "none", children: _jsx("path", { d: "M0 0.5L1076 0.499906", stroke: "var(--color-border-medium, #A3A3A3)", strokeWidth: "1", strokeDasharray: "10 10" }) }))] }, type)); });
|
|
331
|
+
return nonEmptyGroups.map(function (type, groupIndex) { return (_jsxs(React.Fragment, { children: [_jsxs("div", { className: "search-bar-transfer__category-header", children: [_jsx(Icon, { name: categoryInfo[type].icon, size: "md" }), _jsx(Text, { size: "md", variant: "bold", color: "default", as: "span", children: categoryInfo[type].label })] }), groupedTransfers[type].map(function (line) { return (_jsx(TransferLine, { id: line.id, type: line.type, paxData: line.paxData, transferDate: line.transferDate, pickupPoint: line.pickupPoint, dropoffPoint: line.dropoffPoint, locations: locations, onDataChange: function (data) { return handleTransferLineChange(line.id, data); }, onDelete: function () { return handleDeleteTransferLine(line.id); }, showDelete: transferLines.length > 1, checkEmpty: error !== null, scrollOnOpen: scrollOnOpen }, line.id)); }), groupIndex < nonEmptyGroups.length - 1 && (_jsx("svg", { className: "search-bar-transfer__divider", xmlns: "http://www.w3.org/2000/svg", width: "100%", height: "1", viewBox: "0 0 1076 1", fill: "none", preserveAspectRatio: "none", children: _jsx("path", { d: "M0 0.5L1076 0.499906", stroke: "var(--color-border-medium, #A3A3A3)", strokeWidth: "1", strokeDasharray: "10 10" }) }))] }, type)); });
|
|
336
332
|
})() }))] })), _jsxs("div", { className: "search-bar-transfer__actions", children: [_jsx(Checkbox, { checked: sameVehicle, onChange: setSameVehicle, label: "Use the same vehicle for all your transfers" }), _jsxs("div", { className: "search-bar-transfer__cta", children: [error && (_jsx(Toast, { text: error, type: "danger" })), _jsx(Button, { variant: "primary", size: "lg", onClick: handleSearch, children: "Search" })] })] })] }));
|
|
337
333
|
};
|
|
338
334
|
export default SearchBarTransfer;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default } from './SearchBarTransfer';
|
|
2
|
-
export type {
|
|
2
|
+
export type { SearchBarTransferData, SearchBarTransferProps, TransferMode } from './SearchBarTransfer';
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import '../../../styles/components/organism/transfer-line.css';
|
|
3
|
-
import {
|
|
3
|
+
import { LocationGroup, LocationOption } from '../../molecules/LocationDropdown/LocationDropdown';
|
|
4
4
|
import { PaxData } from '../PaxSelector/PaxSelector';
|
|
5
5
|
export type TransferType = 'arrival' | 'departure' | 'inter-hotel';
|
|
6
|
-
export type {
|
|
6
|
+
export type { LocationOption };
|
|
7
7
|
export interface TransferLineData {
|
|
8
8
|
id: string;
|
|
9
9
|
type: TransferType;
|
|
10
10
|
paxData?: PaxData;
|
|
11
11
|
transferDate?: string;
|
|
12
|
-
pickupPoint?:
|
|
13
|
-
dropoffPoint?:
|
|
12
|
+
pickupPoint?: LocationOption;
|
|
13
|
+
dropoffPoint?: LocationOption;
|
|
14
14
|
}
|
|
15
15
|
export interface TransferLineProps {
|
|
16
16
|
/** Unique identifier for this transfer line */
|
|
@@ -21,10 +21,10 @@ export interface TransferLineProps {
|
|
|
21
21
|
paxData?: PaxData;
|
|
22
22
|
/** Transfer date */
|
|
23
23
|
transferDate?: string;
|
|
24
|
-
/** Selected pickup point ID (
|
|
25
|
-
pickupPoint?: string | number;
|
|
26
|
-
/** Selected dropoff point ID (
|
|
27
|
-
dropoffPoint?: string | number;
|
|
24
|
+
/** Selected pickup point - can be an ID (string/number) or LocationOption object */
|
|
25
|
+
pickupPoint?: string | number | LocationOption;
|
|
26
|
+
/** Selected dropoff point - can be an ID (string/number) or LocationOption object */
|
|
27
|
+
dropoffPoint?: string | number | LocationOption;
|
|
28
28
|
/** All location options - will be filtered based on transfer type */
|
|
29
29
|
locations?: {
|
|
30
30
|
options?: LocationOption[];
|
|
@@ -35,9 +35,9 @@ export interface TransferLineProps {
|
|
|
35
35
|
/** Callback when transfer date changes */
|
|
36
36
|
onDateChange?: (date: string) => void;
|
|
37
37
|
/** Callback when pickup point changes */
|
|
38
|
-
onPickupChange?: (location:
|
|
38
|
+
onPickupChange?: (location: LocationOption | null) => void;
|
|
39
39
|
/** Callback when dropoff point changes */
|
|
40
|
-
onDropoffChange?: (location:
|
|
40
|
+
onDropoffChange?: (location: LocationOption | null) => void;
|
|
41
41
|
/** Callback when any data changes - returns complete TransferLineData */
|
|
42
42
|
onDataChange?: (data: TransferLineData) => void;
|
|
43
43
|
/** Callback when delete button is clicked */
|
|
@@ -68,55 +68,61 @@ var TransferLine = function (_a) {
|
|
|
68
68
|
}
|
|
69
69
|
return null;
|
|
70
70
|
};
|
|
71
|
-
//
|
|
71
|
+
// Helper to check if value is LocationOption
|
|
72
|
+
var isLocationOption = function (value) {
|
|
73
|
+
return typeof value === 'object' && value !== null && 'id' in value && 'label' in value && 'type' in value;
|
|
74
|
+
};
|
|
75
|
+
// Initialize location data from IDs, LocationOption objects, or set defaults
|
|
72
76
|
React.useEffect(function () {
|
|
77
|
+
// If pickupPoint is already a LocationOption object, use it directly
|
|
78
|
+
if (isLocationOption(pickupPoint)) {
|
|
79
|
+
// Only update if the ID is different to prevent infinite loops
|
|
80
|
+
if ((internalPickupPoint === null || internalPickupPoint === void 0 ? void 0 : internalPickupPoint.id) !== pickupPoint.id) {
|
|
81
|
+
setInternalPickupPoint(pickupPoint);
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
73
85
|
var _a = filterLocations('pickup'), options = _a.options, groups = _a.groups;
|
|
74
86
|
var allPickupLocations = getAllLocations(options, groups);
|
|
75
87
|
if (pickupPoint) {
|
|
76
88
|
var location_1 = allPickupLocations.find(function (loc) { return loc.id === pickupPoint; });
|
|
77
|
-
if (location_1) {
|
|
78
|
-
setInternalPickupPoint(
|
|
79
|
-
id: location_1.id,
|
|
80
|
-
locationName: location_1.label,
|
|
81
|
-
});
|
|
89
|
+
if (location_1 && (internalPickupPoint === null || internalPickupPoint === void 0 ? void 0 : internalPickupPoint.id) !== location_1.id) {
|
|
90
|
+
setInternalPickupPoint(location_1);
|
|
82
91
|
}
|
|
83
92
|
}
|
|
84
|
-
else {
|
|
85
|
-
// Set default location if applicable
|
|
93
|
+
else if (!internalPickupPoint) {
|
|
94
|
+
// Set default location if applicable (only if not already set)
|
|
86
95
|
var defaultLocation = getDefaultLocation('pickup');
|
|
87
96
|
if (defaultLocation) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
locationName: defaultLocation.label,
|
|
91
|
-
};
|
|
92
|
-
setInternalPickupPoint(defaultData);
|
|
93
|
-
onPickupChange === null || onPickupChange === void 0 ? void 0 : onPickupChange(defaultData);
|
|
97
|
+
setInternalPickupPoint(defaultLocation);
|
|
98
|
+
onPickupChange === null || onPickupChange === void 0 ? void 0 : onPickupChange(defaultLocation);
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
97
102
|
}, [pickupPoint, type, locations]);
|
|
98
103
|
React.useEffect(function () {
|
|
104
|
+
// If dropoffPoint is already a LocationOption object, use it directly
|
|
105
|
+
if (isLocationOption(dropoffPoint)) {
|
|
106
|
+
// Only update if the ID is different to prevent infinite loops
|
|
107
|
+
if ((internalDropoffPoint === null || internalDropoffPoint === void 0 ? void 0 : internalDropoffPoint.id) !== dropoffPoint.id) {
|
|
108
|
+
setInternalDropoffPoint(dropoffPoint);
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
99
112
|
var _a = filterLocations('dropoff'), options = _a.options, groups = _a.groups;
|
|
100
113
|
var allDropoffLocations = getAllLocations(options, groups);
|
|
101
114
|
if (dropoffPoint) {
|
|
102
115
|
var location_2 = allDropoffLocations.find(function (loc) { return loc.id === dropoffPoint; });
|
|
103
|
-
if (location_2) {
|
|
104
|
-
setInternalDropoffPoint(
|
|
105
|
-
id: location_2.id,
|
|
106
|
-
locationName: location_2.label,
|
|
107
|
-
});
|
|
116
|
+
if (location_2 && (internalDropoffPoint === null || internalDropoffPoint === void 0 ? void 0 : internalDropoffPoint.id) !== location_2.id) {
|
|
117
|
+
setInternalDropoffPoint(location_2);
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
|
-
else {
|
|
111
|
-
// Set default location if applicable
|
|
120
|
+
else if (!internalDropoffPoint) {
|
|
121
|
+
// Set default location if applicable (only if not already set)
|
|
112
122
|
var defaultLocation = getDefaultLocation('dropoff');
|
|
113
123
|
if (defaultLocation) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
locationName: defaultLocation.label,
|
|
117
|
-
};
|
|
118
|
-
setInternalDropoffPoint(defaultData);
|
|
119
|
-
onDropoffChange === null || onDropoffChange === void 0 ? void 0 : onDropoffChange(defaultData);
|
|
124
|
+
setInternalDropoffPoint(defaultLocation);
|
|
125
|
+
onDropoffChange === null || onDropoffChange === void 0 ? void 0 : onDropoffChange(defaultLocation);
|
|
120
126
|
}
|
|
121
127
|
}
|
|
122
128
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -174,13 +180,41 @@ var TransferLine = function (_a) {
|
|
|
174
180
|
setInternalTransferDate(dateValue);
|
|
175
181
|
onDateChange === null || onDateChange === void 0 ? void 0 : onDateChange(dateValue);
|
|
176
182
|
};
|
|
183
|
+
// Helper to convert LocationData to LocationOption
|
|
184
|
+
var toLocationOption = function (locationData, position) {
|
|
185
|
+
if (!locationData)
|
|
186
|
+
return null;
|
|
187
|
+
// Try to find in the options to get the type
|
|
188
|
+
var _a = filterLocations(position), options = _a.options, groups = _a.groups;
|
|
189
|
+
var allLocations = getAllLocations(options, groups);
|
|
190
|
+
var found = allLocations.find(function (loc) { return loc.id === locationData.id; });
|
|
191
|
+
if (found)
|
|
192
|
+
return found;
|
|
193
|
+
// If not found, infer the type
|
|
194
|
+
var name = locationData.locationName.toLowerCase();
|
|
195
|
+
var id = String(locationData.id).toLowerCase();
|
|
196
|
+
var locType = 'accommodation';
|
|
197
|
+
if (name.includes('harbour') || name.includes('port') || id.includes('harbour') || id.includes('port')) {
|
|
198
|
+
locType = 'port';
|
|
199
|
+
}
|
|
200
|
+
else if (name.includes('airport') || id.includes('airport')) {
|
|
201
|
+
locType = 'airport';
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
id: locationData.id,
|
|
205
|
+
label: locationData.locationName,
|
|
206
|
+
type: locType,
|
|
207
|
+
};
|
|
208
|
+
};
|
|
177
209
|
var handlePickupChange = function (location) {
|
|
178
|
-
|
|
179
|
-
|
|
210
|
+
var locationOption = toLocationOption(location, 'pickup');
|
|
211
|
+
setInternalPickupPoint(locationOption);
|
|
212
|
+
onPickupChange === null || onPickupChange === void 0 ? void 0 : onPickupChange(locationOption);
|
|
180
213
|
};
|
|
181
214
|
var handleDropoffChange = function (location) {
|
|
182
|
-
|
|
183
|
-
|
|
215
|
+
var locationOption = toLocationOption(location, 'dropoff');
|
|
216
|
+
setInternalDropoffPoint(locationOption);
|
|
217
|
+
onDropoffChange === null || onDropoffChange === void 0 ? void 0 : onDropoffChange(locationOption);
|
|
184
218
|
};
|
|
185
219
|
// Check if inputs are empty (when checkEmpty is true)
|
|
186
220
|
var isPaxEmpty = checkEmpty &&
|
|
@@ -192,13 +226,18 @@ var TransferLine = function (_a) {
|
|
|
192
226
|
var isDateEmpty = checkEmpty && (!internalTransferDate || internalTransferDate === '');
|
|
193
227
|
var isPickupEmpty = checkEmpty && !internalPickupPoint;
|
|
194
228
|
var isDropoffEmpty = checkEmpty && !internalDropoffPoint;
|
|
195
|
-
// Helper function to find LocationOption from ID for defaultValue
|
|
196
|
-
var findLocationOption = function (
|
|
197
|
-
if (!
|
|
229
|
+
// Helper function to find LocationOption from ID or LocationOption for defaultValue
|
|
230
|
+
var findLocationOption = function (value, position) {
|
|
231
|
+
if (!value)
|
|
198
232
|
return undefined;
|
|
233
|
+
// If value is already a LocationOption object, use it directly
|
|
234
|
+
if (isLocationOption(value)) {
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
// Otherwise, look up by ID in the locations
|
|
199
238
|
var _a = filterLocations(position), options = _a.options, groups = _a.groups;
|
|
200
239
|
var allLocations = getAllLocations(options, groups);
|
|
201
|
-
return allLocations.find(function (loc) { return loc.id ===
|
|
240
|
+
return allLocations.find(function (loc) { return loc.id === value; });
|
|
202
241
|
};
|
|
203
242
|
var defaultPickupOption = findLocationOption(pickupPoint, 'pickup');
|
|
204
243
|
var defaultDropoffOption = findLocationOption(dropoffPoint, 'dropoff');
|
package/package.json
CHANGED
|
@@ -88,6 +88,9 @@ const VehicleSupplement: React.FC<VehicleSupplementProps> = ({
|
|
|
88
88
|
|
|
89
89
|
const getTransferTypeIcon = (transferType: string) => {
|
|
90
90
|
const type = transferType.toLowerCase();
|
|
91
|
+
if (type.includes('harbour')) {
|
|
92
|
+
return 'ship';
|
|
93
|
+
}
|
|
91
94
|
if (type.includes('arrival') || type.includes('airport') || type.includes('inbound')) {
|
|
92
95
|
return 'plane-landing-outline';
|
|
93
96
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export * from './Booking';
|
|
2
|
-
export type {
|
|
2
|
+
export type {
|
|
3
|
+
BookingDocketItem,
|
|
4
|
+
BookingDocketProps,
|
|
5
|
+
BookingResumeData,
|
|
6
|
+
} from './BookingDocket/BookingDocket';
|
|
3
7
|
export type { BookingStepProps } from './BookingStep/BookingStep';
|
|
@@ -9,15 +9,15 @@ import LocationDropdown, {
|
|
|
9
9
|
import DateTimePicker from "../DateTimePicker/DateTimePicker";
|
|
10
10
|
import PaxSelector, { PaxData } from "../PaxSelector/PaxSelector";
|
|
11
11
|
|
|
12
|
-
// Re-export
|
|
13
|
-
export type {
|
|
12
|
+
// Re-export LocationOption for convenience
|
|
13
|
+
export type { LocationOption };
|
|
14
14
|
|
|
15
15
|
export interface RoundTripTransfer {
|
|
16
16
|
type: "arrival" | "departure";
|
|
17
17
|
paxData?: PaxData;
|
|
18
18
|
transferDate?: string;
|
|
19
|
-
pickupPoint?:
|
|
20
|
-
dropoffPoint?:
|
|
19
|
+
pickupPoint?: LocationOption;
|
|
20
|
+
dropoffPoint?: LocationOption;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface RoundTripData {
|
|
@@ -25,8 +25,8 @@ export interface RoundTripData {
|
|
|
25
25
|
paxData?: PaxData;
|
|
26
26
|
arrivalDate?: string;
|
|
27
27
|
departureDate?: string;
|
|
28
|
-
pickupDropoffPoint?:
|
|
29
|
-
accommodation?:
|
|
28
|
+
pickupDropoffPoint?: LocationOption;
|
|
29
|
+
accommodation?: LocationOption;
|
|
30
30
|
transfers: [RoundTripTransfer, RoundTripTransfer];
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -39,10 +39,10 @@ export interface RoundTripProps {
|
|
|
39
39
|
arrivalDate?: string;
|
|
40
40
|
/** Departure date */
|
|
41
41
|
departureDate?: string;
|
|
42
|
-
/** Selected pickup/dropoff point ID
|
|
43
|
-
pickupDropoffPoint?: string;
|
|
44
|
-
/** Selected accommodation ID
|
|
45
|
-
accommodation?: string;
|
|
42
|
+
/** Selected pickup/dropoff point - can be an ID string or LocationOption object */
|
|
43
|
+
pickupDropoffPoint?: string | LocationOption;
|
|
44
|
+
/** Selected accommodation - can be an ID string or LocationOption object */
|
|
45
|
+
accommodation?: string | LocationOption;
|
|
46
46
|
/** All location options */
|
|
47
47
|
locations?: {
|
|
48
48
|
options?: LocationOption[];
|
|
@@ -53,9 +53,9 @@ export interface RoundTripProps {
|
|
|
53
53
|
/** Callback when dates change */
|
|
54
54
|
onDatesChange?: (arrivalDate: string, departureDate: string) => void;
|
|
55
55
|
/** Callback when pickup/dropoff point changes */
|
|
56
|
-
onPickupDropoffChange?: (location:
|
|
56
|
+
onPickupDropoffChange?: (location: LocationOption | null) => void;
|
|
57
57
|
/** Callback when accommodation changes */
|
|
58
|
-
onAccommodationChange?: (location:
|
|
58
|
+
onAccommodationChange?: (location: LocationOption | null) => void;
|
|
59
59
|
/** Callback when any data changes - returns array of 2 transfers */
|
|
60
60
|
onChange?: (data: RoundTripData) => void;
|
|
61
61
|
/** Additional CSS classes */
|
|
@@ -93,9 +93,9 @@ const RoundTrip: React.FC<RoundTripProps> = ({
|
|
|
93
93
|
string | undefined
|
|
94
94
|
>(departureDate);
|
|
95
95
|
const [internalPickupDropoffPoint, setInternalPickupDropoffPoint] =
|
|
96
|
-
useState<
|
|
96
|
+
useState<LocationOption | null>(null);
|
|
97
97
|
const [internalAccommodation, setInternalAccommodation] =
|
|
98
|
-
useState<
|
|
98
|
+
useState<LocationOption | null>(null);
|
|
99
99
|
|
|
100
100
|
// Helper function to get all locations (from options and groups)
|
|
101
101
|
const getAllLocations = (
|
|
@@ -165,8 +165,22 @@ const RoundTrip: React.FC<RoundTripProps> = ({
|
|
|
165
165
|
};
|
|
166
166
|
};
|
|
167
167
|
|
|
168
|
-
//
|
|
168
|
+
// Helper to check if value is LocationOption
|
|
169
|
+
const isLocationOption = (value: string | LocationOption | undefined): value is LocationOption => {
|
|
170
|
+
return typeof value === 'object' && value !== null && 'id' in value && 'label' in value && 'type' in value;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Initialize location data from IDs or LocationOption objects
|
|
169
174
|
useEffect(() => {
|
|
175
|
+
// If pickupDropoffPoint is already a LocationOption object, use it directly
|
|
176
|
+
if (isLocationOption(pickupDropoffPoint)) {
|
|
177
|
+
// Only update if the ID is different to prevent infinite loops
|
|
178
|
+
if (internalPickupDropoffPoint?.id !== pickupDropoffPoint.id) {
|
|
179
|
+
setInternalPickupDropoffPoint(pickupDropoffPoint);
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
170
184
|
const { options, groups } = getPickupDropoffOptions();
|
|
171
185
|
const allLocations = getAllLocations(options, groups);
|
|
172
186
|
|
|
@@ -174,38 +188,37 @@ const RoundTrip: React.FC<RoundTripProps> = ({
|
|
|
174
188
|
const location = allLocations.find(
|
|
175
189
|
(loc) => loc.id === pickupDropoffPoint
|
|
176
190
|
);
|
|
177
|
-
if (location) {
|
|
178
|
-
setInternalPickupDropoffPoint(
|
|
179
|
-
id: location.id,
|
|
180
|
-
locationName: location.label,
|
|
181
|
-
});
|
|
191
|
+
if (location && internalPickupDropoffPoint?.id !== location.id) {
|
|
192
|
+
setInternalPickupDropoffPoint(location);
|
|
182
193
|
}
|
|
183
|
-
} else {
|
|
184
|
-
// Set default to first airport if available
|
|
194
|
+
} else if (!internalPickupDropoffPoint) {
|
|
195
|
+
// Set default to first airport if available (only if not already set)
|
|
185
196
|
const defaultAirport = allLocations.find((loc) => loc.type === "airport");
|
|
186
197
|
if (defaultAirport) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
locationName: defaultAirport.label,
|
|
190
|
-
};
|
|
191
|
-
setInternalPickupDropoffPoint(defaultData);
|
|
192
|
-
onPickupDropoffChange?.(defaultData);
|
|
198
|
+
setInternalPickupDropoffPoint(defaultAirport);
|
|
199
|
+
onPickupDropoffChange?.(defaultAirport);
|
|
193
200
|
}
|
|
194
201
|
}
|
|
195
202
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
196
203
|
}, [pickupDropoffPoint, locations]);
|
|
197
204
|
|
|
198
205
|
useEffect(() => {
|
|
206
|
+
// If accommodation is already a LocationOption object, use it directly
|
|
207
|
+
if (isLocationOption(accommodation)) {
|
|
208
|
+
// Only update if the ID is different to prevent infinite loops
|
|
209
|
+
if (internalAccommodation?.id !== accommodation.id) {
|
|
210
|
+
setInternalAccommodation(accommodation);
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
199
215
|
const { options, groups } = getAccommodationOptions();
|
|
200
216
|
const allLocations = getAllLocations(options, groups);
|
|
201
217
|
|
|
202
218
|
if (accommodation) {
|
|
203
219
|
const location = allLocations.find((loc) => loc.id === accommodation);
|
|
204
|
-
if (location) {
|
|
205
|
-
setInternalAccommodation(
|
|
206
|
-
id: location.id,
|
|
207
|
-
locationName: location.label,
|
|
208
|
-
});
|
|
220
|
+
if (location && internalAccommodation?.id !== location.id) {
|
|
221
|
+
setInternalAccommodation(location);
|
|
209
222
|
}
|
|
210
223
|
}
|
|
211
224
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -265,14 +278,43 @@ const RoundTrip: React.FC<RoundTripProps> = ({
|
|
|
265
278
|
}
|
|
266
279
|
};
|
|
267
280
|
|
|
281
|
+
// Helper to convert LocationData to LocationOption
|
|
282
|
+
const toLocationOption = (locationData: LocationData | null, locationType: 'pickup-dropoff' | 'accommodation'): LocationOption | null => {
|
|
283
|
+
if (!locationData) return null;
|
|
284
|
+
|
|
285
|
+
// Try to find in the options to get the type
|
|
286
|
+
const { options, groups } = locationType === 'pickup-dropoff' ? getPickupDropoffOptions() : getAccommodationOptions();
|
|
287
|
+
const allLocations = getAllLocations(options, groups);
|
|
288
|
+
const found = allLocations.find((loc) => loc.id === locationData.id);
|
|
289
|
+
if (found) return found;
|
|
290
|
+
|
|
291
|
+
// If not found, infer the type
|
|
292
|
+
const name = locationData.locationName.toLowerCase();
|
|
293
|
+
const id = String(locationData.id).toLowerCase();
|
|
294
|
+
let type: 'airport' | 'port' | 'accommodation' = 'accommodation';
|
|
295
|
+
if (name.includes('harbour') || name.includes('port') || id.includes('harbour') || id.includes('port')) {
|
|
296
|
+
type = 'port';
|
|
297
|
+
} else if (name.includes('airport') || id.includes('airport')) {
|
|
298
|
+
type = 'airport';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
id: locationData.id,
|
|
303
|
+
label: locationData.locationName,
|
|
304
|
+
type,
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
|
|
268
308
|
const handlePickupDropoffChange = (location: LocationData | null) => {
|
|
269
|
-
|
|
270
|
-
|
|
309
|
+
const locationOption = toLocationOption(location, 'pickup-dropoff');
|
|
310
|
+
setInternalPickupDropoffPoint(locationOption);
|
|
311
|
+
onPickupDropoffChange?.(locationOption);
|
|
271
312
|
};
|
|
272
313
|
|
|
273
314
|
const handleAccommodationChange = (location: LocationData | null) => {
|
|
274
|
-
|
|
275
|
-
|
|
315
|
+
const locationOption = toLocationOption(location, 'accommodation');
|
|
316
|
+
setInternalAccommodation(locationOption);
|
|
317
|
+
onAccommodationChange?.(locationOption);
|
|
276
318
|
};
|
|
277
319
|
|
|
278
320
|
// Check if inputs are empty (when checkEmpty is true)
|
|
@@ -292,12 +334,19 @@ const RoundTrip: React.FC<RoundTripProps> = ({
|
|
|
292
334
|
const pickupDropoffOptions = getPickupDropoffOptions();
|
|
293
335
|
const accommodationOptions = getAccommodationOptions();
|
|
294
336
|
|
|
295
|
-
// Helper function to find LocationOption from ID for defaultValue
|
|
296
|
-
const findLocationOption = (
|
|
297
|
-
if (!
|
|
337
|
+
// Helper function to find LocationOption from ID or LocationOption for defaultValue
|
|
338
|
+
const findLocationOption = (value: string | LocationOption | undefined, locationType: 'pickup-dropoff' | 'accommodation'): LocationOption | undefined => {
|
|
339
|
+
if (!value) return undefined;
|
|
340
|
+
|
|
341
|
+
// If value is already a LocationOption object, use it directly
|
|
342
|
+
if (isLocationOption(value)) {
|
|
343
|
+
return value;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Otherwise, look up by ID in the locations
|
|
298
347
|
const { options, groups } = locationType === 'pickup-dropoff' ? getPickupDropoffOptions() : getAccommodationOptions();
|
|
299
348
|
const allLocations = getAllLocations(options, groups);
|
|
300
|
-
return allLocations.find((loc) => loc.id ===
|
|
349
|
+
return allLocations.find((loc) => loc.id === value);
|
|
301
350
|
};
|
|
302
351
|
|
|
303
352
|
const defaultPickupDropoffOption = findLocationOption(pickupDropoffPoint, 'pickup-dropoff');
|
|
@@ -430,8 +430,8 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
430
430
|
paxData={roundTripData?.paxData}
|
|
431
431
|
arrivalDate={roundTripData?.arrivalDate}
|
|
432
432
|
departureDate={roundTripData?.departureDate}
|
|
433
|
-
pickupDropoffPoint={roundTripData?.pickupDropoffPoint
|
|
434
|
-
accommodation={roundTripData?.accommodation
|
|
433
|
+
pickupDropoffPoint={roundTripData?.pickupDropoffPoint}
|
|
434
|
+
accommodation={roundTripData?.accommodation}
|
|
435
435
|
onChange={setRoundTripData}
|
|
436
436
|
checkEmpty={error !== null}
|
|
437
437
|
scrollOnOpen={scrollOnOpen}
|
|
@@ -508,8 +508,8 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
508
508
|
type={line.type}
|
|
509
509
|
paxData={line.paxData}
|
|
510
510
|
transferDate={line.transferDate}
|
|
511
|
-
pickupPoint={line.pickupPoint
|
|
512
|
-
dropoffPoint={line.dropoffPoint
|
|
511
|
+
pickupPoint={line.pickupPoint}
|
|
512
|
+
dropoffPoint={line.dropoffPoint}
|
|
513
513
|
locations={locations}
|
|
514
514
|
onDataChange={(data) => handleTransferLineChange(line.id, data)}
|
|
515
515
|
onDelete={() => handleDeleteTransferLine(line.id)}
|
|
@@ -12,16 +12,16 @@ import PaxSelector, { PaxData } from '../PaxSelector/PaxSelector';
|
|
|
12
12
|
|
|
13
13
|
export type TransferType = 'arrival' | 'departure' | 'inter-hotel';
|
|
14
14
|
|
|
15
|
-
// Re-export
|
|
16
|
-
export type {
|
|
15
|
+
// Re-export LocationOption for convenience
|
|
16
|
+
export type { LocationOption };
|
|
17
17
|
|
|
18
18
|
export interface TransferLineData {
|
|
19
19
|
id: string;
|
|
20
20
|
type: TransferType;
|
|
21
21
|
paxData?: PaxData;
|
|
22
22
|
transferDate?: string;
|
|
23
|
-
pickupPoint?:
|
|
24
|
-
dropoffPoint?:
|
|
23
|
+
pickupPoint?: LocationOption;
|
|
24
|
+
dropoffPoint?: LocationOption;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface TransferLineProps {
|
|
@@ -33,10 +33,10 @@ export interface TransferLineProps {
|
|
|
33
33
|
paxData?: PaxData;
|
|
34
34
|
/** Transfer date */
|
|
35
35
|
transferDate?: string;
|
|
36
|
-
/** Selected pickup point ID (
|
|
37
|
-
pickupPoint?: string | number;
|
|
38
|
-
/** Selected dropoff point ID (
|
|
39
|
-
dropoffPoint?: string | number;
|
|
36
|
+
/** Selected pickup point - can be an ID (string/number) or LocationOption object */
|
|
37
|
+
pickupPoint?: string | number | LocationOption;
|
|
38
|
+
/** Selected dropoff point - can be an ID (string/number) or LocationOption object */
|
|
39
|
+
dropoffPoint?: string | number | LocationOption;
|
|
40
40
|
/** All location options - will be filtered based on transfer type */
|
|
41
41
|
locations?: {
|
|
42
42
|
options?: LocationOption[];
|
|
@@ -47,9 +47,9 @@ export interface TransferLineProps {
|
|
|
47
47
|
/** Callback when transfer date changes */
|
|
48
48
|
onDateChange?: (date: string) => void;
|
|
49
49
|
/** Callback when pickup point changes */
|
|
50
|
-
onPickupChange?: (location:
|
|
50
|
+
onPickupChange?: (location: LocationOption | null) => void;
|
|
51
51
|
/** Callback when dropoff point changes */
|
|
52
|
-
onDropoffChange?: (location:
|
|
52
|
+
onDropoffChange?: (location: LocationOption | null) => void;
|
|
53
53
|
/** Callback when any data changes - returns complete TransferLineData */
|
|
54
54
|
onDataChange?: (data: TransferLineData) => void;
|
|
55
55
|
/** Callback when delete button is clicked */
|
|
@@ -93,10 +93,10 @@ const TransferLine: React.FC<TransferLineProps> = ({
|
|
|
93
93
|
const [internalTransferDate, setInternalTransferDate] = useState<string | undefined>(
|
|
94
94
|
transferDate
|
|
95
95
|
);
|
|
96
|
-
const [internalPickupPoint, setInternalPickupPoint] = useState<
|
|
96
|
+
const [internalPickupPoint, setInternalPickupPoint] = useState<LocationOption | null>(
|
|
97
97
|
null
|
|
98
98
|
);
|
|
99
|
-
const [internalDropoffPoint, setInternalDropoffPoint] = useState<
|
|
99
|
+
const [internalDropoffPoint, setInternalDropoffPoint] = useState<LocationOption | null>(
|
|
100
100
|
null
|
|
101
101
|
);
|
|
102
102
|
|
|
@@ -153,56 +153,65 @@ const TransferLine: React.FC<TransferLineProps> = ({
|
|
|
153
153
|
return null;
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
-
//
|
|
156
|
+
// Helper to check if value is LocationOption
|
|
157
|
+
const isLocationOption = (value: string | number | LocationOption | undefined): value is LocationOption => {
|
|
158
|
+
return typeof value === 'object' && value !== null && 'id' in value && 'label' in value && 'type' in value;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Initialize location data from IDs, LocationOption objects, or set defaults
|
|
157
162
|
React.useEffect(() => {
|
|
163
|
+
// If pickupPoint is already a LocationOption object, use it directly
|
|
164
|
+
if (isLocationOption(pickupPoint)) {
|
|
165
|
+
// Only update if the ID is different to prevent infinite loops
|
|
166
|
+
if (internalPickupPoint?.id !== pickupPoint.id) {
|
|
167
|
+
setInternalPickupPoint(pickupPoint);
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
158
172
|
const { options, groups } = filterLocations('pickup');
|
|
159
173
|
const allPickupLocations = getAllLocations(options, groups);
|
|
160
174
|
|
|
161
175
|
if (pickupPoint) {
|
|
162
176
|
const location = allPickupLocations.find((loc) => loc.id === pickupPoint);
|
|
163
|
-
if (location) {
|
|
164
|
-
setInternalPickupPoint(
|
|
165
|
-
id: location.id,
|
|
166
|
-
locationName: location.label,
|
|
167
|
-
});
|
|
177
|
+
if (location && internalPickupPoint?.id !== location.id) {
|
|
178
|
+
setInternalPickupPoint(location);
|
|
168
179
|
}
|
|
169
|
-
} else {
|
|
170
|
-
// Set default location if applicable
|
|
180
|
+
} else if (!internalPickupPoint) {
|
|
181
|
+
// Set default location if applicable (only if not already set)
|
|
171
182
|
const defaultLocation = getDefaultLocation('pickup');
|
|
172
183
|
if (defaultLocation) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
locationName: defaultLocation.label,
|
|
176
|
-
};
|
|
177
|
-
setInternalPickupPoint(defaultData);
|
|
178
|
-
onPickupChange?.(defaultData);
|
|
184
|
+
setInternalPickupPoint(defaultLocation);
|
|
185
|
+
onPickupChange?.(defaultLocation);
|
|
179
186
|
}
|
|
180
187
|
}
|
|
181
188
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
182
189
|
}, [pickupPoint, type, locations]);
|
|
183
190
|
|
|
184
191
|
React.useEffect(() => {
|
|
192
|
+
// If dropoffPoint is already a LocationOption object, use it directly
|
|
193
|
+
if (isLocationOption(dropoffPoint)) {
|
|
194
|
+
// Only update if the ID is different to prevent infinite loops
|
|
195
|
+
if (internalDropoffPoint?.id !== dropoffPoint.id) {
|
|
196
|
+
setInternalDropoffPoint(dropoffPoint);
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
185
201
|
const { options, groups } = filterLocations('dropoff');
|
|
186
202
|
const allDropoffLocations = getAllLocations(options, groups);
|
|
187
203
|
|
|
188
204
|
if (dropoffPoint) {
|
|
189
205
|
const location = allDropoffLocations.find((loc) => loc.id === dropoffPoint);
|
|
190
|
-
if (location) {
|
|
191
|
-
setInternalDropoffPoint(
|
|
192
|
-
id: location.id,
|
|
193
|
-
locationName: location.label,
|
|
194
|
-
});
|
|
206
|
+
if (location && internalDropoffPoint?.id !== location.id) {
|
|
207
|
+
setInternalDropoffPoint(location);
|
|
195
208
|
}
|
|
196
|
-
} else {
|
|
197
|
-
// Set default location if applicable
|
|
209
|
+
} else if (!internalDropoffPoint) {
|
|
210
|
+
// Set default location if applicable (only if not already set)
|
|
198
211
|
const defaultLocation = getDefaultLocation('dropoff');
|
|
199
212
|
if (defaultLocation) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
locationName: defaultLocation.label,
|
|
203
|
-
};
|
|
204
|
-
setInternalDropoffPoint(defaultData);
|
|
205
|
-
onDropoffChange?.(defaultData);
|
|
213
|
+
setInternalDropoffPoint(defaultLocation);
|
|
214
|
+
onDropoffChange?.(defaultLocation);
|
|
206
215
|
}
|
|
207
216
|
}
|
|
208
217
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -266,14 +275,43 @@ const TransferLine: React.FC<TransferLineProps> = ({
|
|
|
266
275
|
onDateChange?.(dateValue);
|
|
267
276
|
};
|
|
268
277
|
|
|
278
|
+
// Helper to convert LocationData to LocationOption
|
|
279
|
+
const toLocationOption = (locationData: LocationData | null, position: 'pickup' | 'dropoff'): LocationOption | null => {
|
|
280
|
+
if (!locationData) return null;
|
|
281
|
+
|
|
282
|
+
// Try to find in the options to get the type
|
|
283
|
+
const { options, groups } = filterLocations(position);
|
|
284
|
+
const allLocations = getAllLocations(options, groups);
|
|
285
|
+
const found = allLocations.find((loc) => loc.id === locationData.id);
|
|
286
|
+
if (found) return found;
|
|
287
|
+
|
|
288
|
+
// If not found, infer the type
|
|
289
|
+
const name = locationData.locationName.toLowerCase();
|
|
290
|
+
const id = String(locationData.id).toLowerCase();
|
|
291
|
+
let locType: 'airport' | 'port' | 'accommodation' = 'accommodation';
|
|
292
|
+
if (name.includes('harbour') || name.includes('port') || id.includes('harbour') || id.includes('port')) {
|
|
293
|
+
locType = 'port';
|
|
294
|
+
} else if (name.includes('airport') || id.includes('airport')) {
|
|
295
|
+
locType = 'airport';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
id: locationData.id,
|
|
300
|
+
label: locationData.locationName,
|
|
301
|
+
type: locType,
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
|
|
269
305
|
const handlePickupChange = (location: LocationData | null) => {
|
|
270
|
-
|
|
271
|
-
|
|
306
|
+
const locationOption = toLocationOption(location, 'pickup');
|
|
307
|
+
setInternalPickupPoint(locationOption);
|
|
308
|
+
onPickupChange?.(locationOption);
|
|
272
309
|
};
|
|
273
310
|
|
|
274
311
|
const handleDropoffChange = (location: LocationData | null) => {
|
|
275
|
-
|
|
276
|
-
|
|
312
|
+
const locationOption = toLocationOption(location, 'dropoff');
|
|
313
|
+
setInternalDropoffPoint(locationOption);
|
|
314
|
+
onDropoffChange?.(locationOption);
|
|
277
315
|
};
|
|
278
316
|
|
|
279
317
|
// Check if inputs are empty (when checkEmpty is true)
|
|
@@ -292,12 +330,19 @@ const TransferLine: React.FC<TransferLineProps> = ({
|
|
|
292
330
|
|
|
293
331
|
const isDropoffEmpty = checkEmpty && !internalDropoffPoint;
|
|
294
332
|
|
|
295
|
-
// Helper function to find LocationOption from ID for defaultValue
|
|
296
|
-
const findLocationOption = (
|
|
297
|
-
if (!
|
|
333
|
+
// Helper function to find LocationOption from ID or LocationOption for defaultValue
|
|
334
|
+
const findLocationOption = (value: string | number | LocationOption | undefined, position: 'pickup' | 'dropoff'): LocationOption | undefined => {
|
|
335
|
+
if (!value) return undefined;
|
|
336
|
+
|
|
337
|
+
// If value is already a LocationOption object, use it directly
|
|
338
|
+
if (isLocationOption(value)) {
|
|
339
|
+
return value;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Otherwise, look up by ID in the locations
|
|
298
343
|
const { options, groups } = filterLocations(position);
|
|
299
344
|
const allLocations = getAllLocations(options, groups);
|
|
300
|
-
return allLocations.find((loc) => loc.id ===
|
|
345
|
+
return allLocations.find((loc) => loc.id === value);
|
|
301
346
|
};
|
|
302
347
|
|
|
303
348
|
const defaultPickupOption = findLocationOption(pickupPoint, 'pickup');
|