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.
@@ -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 { LocationData, LocationGroup, LocationOption } from "../../molecules/LocationDropdown/LocationDropdown";
3
+ import { LocationGroup, LocationOption } from "../../molecules/LocationDropdown/LocationDropdown";
4
4
  import { PaxData } from "../PaxSelector/PaxSelector";
5
- export type { LocationData };
5
+ export type { LocationOption };
6
6
  export interface RoundTripTransfer {
7
7
  type: "arrival" | "departure";
8
8
  paxData?: PaxData;
9
9
  transferDate?: string;
10
- pickupPoint?: LocationData;
11
- dropoffPoint?: LocationData;
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?: LocationData;
19
- accommodation?: LocationData;
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 (for initial value) */
32
- pickupDropoffPoint?: string;
33
- /** Selected accommodation ID (for initial value) */
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: LocationData | null) => void;
45
+ onPickupDropoffChange?: (location: LocationOption | null) => void;
46
46
  /** Callback when accommodation changes */
47
- onAccommodationChange?: (location: LocationData | null) => void;
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
- // Initialize location data from IDs
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
- var defaultData = {
96
- id: defaultAirport.id,
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
- setInternalPickupDropoffPoint(location);
169
- onPickupDropoffChange === null || onPickupDropoffChange === void 0 ? void 0 : onPickupDropoffChange(location);
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
- setInternalAccommodation(location);
173
- onAccommodationChange === null || onAccommodationChange === void 0 ? void 0 : onAccommodationChange(location);
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 (id, locationType) {
189
- if (!id)
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 === 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 _j = useState(initialMode), mode = _j[0], setMode = _j[1];
48
- var _k = useState(defaultSameVehicle), sameVehicle = _k[0], setSameVehicle = _k[1];
49
- var _l = useState(defaultRoundTripData), roundTripData = _l[0], setRoundTripData = _l[1];
50
- var _m = useState(initializeTransferLines(defaultTransferLines)), transferLines = _m[0], setTransferLines = _m[1];
51
- var _o = useState(null), error = _o[0], setError = _o[1];
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: (_b = roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.pickupDropoffPoint) === null || _b === void 0 ? void 0 : _b.id, accommodation: (_c = roundTripData === null || roundTripData === void 0 ? void 0 : roundTripData.accommodation) === null || _c === void 0 ? void 0 : _c.id, 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 () {
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 { SearchBarTransferProps, SearchBarTransferData, TransferMode } from './SearchBarTransfer';
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 { LocationData, LocationGroup, LocationOption } from '../../molecules/LocationDropdown/LocationDropdown';
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 { LocationData };
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?: LocationData;
13
- dropoffPoint?: LocationData;
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 (for initial value) */
25
- pickupPoint?: string | number;
26
- /** Selected dropoff point ID (for initial value) */
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: LocationData | null) => void;
38
+ onPickupChange?: (location: LocationOption | null) => void;
39
39
  /** Callback when dropoff point changes */
40
- onDropoffChange?: (location: LocationData | null) => void;
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
- // Initialize location data from IDs or set defaults
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
- var defaultData = {
89
- id: defaultLocation.id,
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
- var defaultData = {
115
- id: defaultLocation.id,
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
- setInternalPickupPoint(location);
179
- onPickupChange === null || onPickupChange === void 0 ? void 0 : onPickupChange(location);
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
- setInternalDropoffPoint(location);
183
- onDropoffChange === null || onDropoffChange === void 0 ? void 0 : onDropoffChange(location);
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 (id, position) {
197
- if (!id)
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 === 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mautourco-components",
3
- "version": "0.2.86",
3
+ "version": "0.2.88",
4
4
  "private": false,
5
5
  "description": "Bibliothèque de composants Mautourco pour le redesign",
6
6
  "main": "dist/index.js",
@@ -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 { BookingDocketProps } from './BookingDocket/BookingDocket';
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 LocationData for convenience
13
- export type { LocationData };
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?: LocationData;
20
- dropoffPoint?: LocationData;
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?: LocationData;
29
- accommodation?: LocationData;
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 (for initial value) */
43
- pickupDropoffPoint?: string;
44
- /** Selected accommodation ID (for initial value) */
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: LocationData | null) => void;
56
+ onPickupDropoffChange?: (location: LocationOption | null) => void;
57
57
  /** Callback when accommodation changes */
58
- onAccommodationChange?: (location: LocationData | null) => void;
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<LocationData | null>(null);
96
+ useState<LocationOption | null>(null);
97
97
  const [internalAccommodation, setInternalAccommodation] =
98
- useState<LocationData | null>(null);
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
- // Initialize location data from IDs
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
- const defaultData: LocationData = {
188
- id: defaultAirport.id,
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
- setInternalPickupDropoffPoint(location);
270
- onPickupDropoffChange?.(location);
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
- setInternalAccommodation(location);
275
- onAccommodationChange?.(location);
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 = (id: string | undefined, locationType: 'pickup-dropoff' | 'accommodation'): LocationOption | undefined => {
297
- if (!id) return undefined;
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 === 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?.id as string}
434
- accommodation={roundTripData?.accommodation?.id as string}
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?.id}
512
- dropoffPoint={line.dropoffPoint?.id}
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)}
@@ -1,5 +1,5 @@
1
1
  export { default } from './SearchBarTransfer';
2
- export type { SearchBarTransferProps, SearchBarTransferData, TransferMode } from './SearchBarTransfer';
2
+ export type { SearchBarTransferData, SearchBarTransferProps, TransferMode } from './SearchBarTransfer';
3
3
 
4
4
 
5
5
 
@@ -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 LocationData for convenience
16
- export type { LocationData };
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?: LocationData;
24
- dropoffPoint?: LocationData;
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 (for initial value) */
37
- pickupPoint?: string | number;
38
- /** Selected dropoff point ID (for initial value) */
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: LocationData | null) => void;
50
+ onPickupChange?: (location: LocationOption | null) => void;
51
51
  /** Callback when dropoff point changes */
52
- onDropoffChange?: (location: LocationData | null) => void;
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<LocationData | null>(
96
+ const [internalPickupPoint, setInternalPickupPoint] = useState<LocationOption | null>(
97
97
  null
98
98
  );
99
- const [internalDropoffPoint, setInternalDropoffPoint] = useState<LocationData | null>(
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
- // Initialize location data from IDs or set defaults
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
- const defaultData: LocationData = {
174
- id: defaultLocation.id,
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
- const defaultData: LocationData = {
201
- id: defaultLocation.id,
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
- setInternalPickupPoint(location);
271
- onPickupChange?.(location);
306
+ const locationOption = toLocationOption(location, 'pickup');
307
+ setInternalPickupPoint(locationOption);
308
+ onPickupChange?.(locationOption);
272
309
  };
273
310
 
274
311
  const handleDropoffChange = (location: LocationData | null) => {
275
- setInternalDropoffPoint(location);
276
- onDropoffChange?.(location);
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 = (id: string | number | undefined, position: 'pickup' | 'dropoff'): LocationOption | undefined => {
297
- if (!id) return undefined;
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 === id);
345
+ return allLocations.find((loc) => loc.id === value);
301
346
  };
302
347
 
303
348
  const defaultPickupOption = findLocationOption(pickupPoint, 'pickup');