mautourco-components 0.2.99 → 0.2.101
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/atoms/Inputs/Input/Input.js +9 -1
- package/dist/components/organisms/DateTimePicker/DateTimePicker.js +1 -1
- package/dist/components/organisms/DialogDeleteConfirm/DialogDeleteConfirmMultiple/DialogDeleteConfirmMultiple.js +1 -1
- package/dist/components/organisms/DialogDeleteConfirm/DialogDeleteConfirmWrapper.d.ts +1 -0
- package/dist/components/organisms/DialogDeleteConfirm/DialogDeleteConfirmWrapper.js +2 -2
- package/dist/components/organisms/DialogQuoteRename/DialogQuoteRename.js +5 -2
- package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.d.ts +6 -7
- package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.js +58 -58
- package/package.json +1 -1
- package/src/components/atoms/Inputs/Input/Input.tsx +10 -1
- package/src/components/organisms/DateTimePicker/DateTimePicker.tsx +2 -2
- package/src/components/organisms/DialogDeleteConfirm/DialogDeleteConfirmMultiple/DialogDeleteConfirmMultiple.tsx +4 -1
- package/src/components/organisms/DialogDeleteConfirm/DialogDeleteConfirmWrapper.tsx +3 -1
- package/src/components/organisms/DialogQuoteRename/DialogQuoteRename.tsx +11 -2
- package/src/components/organisms/SearchBarTransfer/SearchBarTransfer.tsx +158 -145
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
2
3
|
import Icon from '../../Icon/Icon';
|
|
3
4
|
var Input = function (_a) {
|
|
4
5
|
var _b = _a.variant, variant = _b === void 0 ? 'default' : _b, _c = _a.placeholder, placeholder = _c === void 0 ? 'Input text' : _c, value = _a.value, _d = _a.disabled, disabled = _d === void 0 ? false : _d, _e = _a.className, className = _e === void 0 ? '' : _e, onChange = _a.onChange, onFocus = _a.onFocus, onBlur = _a.onBlur, icon = _a.icon, _f = _a.iconPosition, iconPosition = _f === void 0 ? 'trailing' : _f, _g = _a.type, type = _g === void 0 ? 'text' : _g, id = _a.id;
|
|
6
|
+
var inputRef = useRef(null);
|
|
5
7
|
var baseClasses = 'input-field';
|
|
6
8
|
var variantClasses = {
|
|
7
9
|
default: 'input-field--default',
|
|
@@ -11,6 +13,12 @@ var Input = function (_a) {
|
|
|
11
13
|
disabled: 'input-field--disabled',
|
|
12
14
|
};
|
|
13
15
|
var inputClasses = "".concat(baseClasses, " ").concat(variantClasses[variant], " ").concat(icon ? "input-field--with-icon input-field--icon-".concat(iconPosition) : '', " ").concat(className).trim();
|
|
14
|
-
|
|
16
|
+
useEffect(function () {
|
|
17
|
+
setTimeout(function () {
|
|
18
|
+
var _a;
|
|
19
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
20
|
+
}, 0);
|
|
21
|
+
}, [value]);
|
|
22
|
+
return (_jsxs("div", { className: "input-wrapper ".concat(icon ? 'input-wrapper--with-icon' : '').trim(), children: [icon && iconPosition === 'leading' && (_jsx("span", { className: "input-icon input-icon--leading", children: _jsx(Icon, { name: icon, size: "sm" }) })), _jsx("input", { ref: inputRef, id: id, type: type, className: inputClasses, placeholder: placeholder, value: value, disabled: disabled || variant === 'disabled', onInput: onChange, onFocus: onFocus, onBlur: onBlur }), icon && iconPosition === 'trailing' && (_jsx("span", { className: "input-icon input-icon--trailing", children: _jsx(Icon, { name: icon, size: "sm" }) }))] }));
|
|
15
23
|
};
|
|
16
24
|
export default Input;
|
|
@@ -109,6 +109,6 @@ var DateTimePicker = function (_a) {
|
|
|
109
109
|
ensurePopoverVisible(popoverContentRef, triggerRef, isOpen);
|
|
110
110
|
}
|
|
111
111
|
}, [isOpen]);
|
|
112
|
-
return (_jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("div", { ref: triggerRef, children: _jsx(CalendarInput, { placeholder: placeholder, value: value, disabled: disabled, state: state, className: inputClassName, iconBGFull: iconBGFull, iconPosition: iconPosition, showChevron: showChevron, isOpen: isOpen, icon: icon }) }) }), _jsx(PopoverContent, { ref: popoverContentRef, className: "w-auto border-transparent !p-0", side: "bottom", align: "start", sideOffset: 8, avoidCollisions: false, children: _jsx(DateTime, { mode: mode, selectionMode: selectionMode, numberOfMonths: numberOfMonths, disableBeforeToday: disableBeforeToday, disableToday: disableToday, onChange: handleDtChange, defaultDateRange: selectedDateRange, defaultTime: selectedTime }) })] }));
|
|
112
|
+
return (_jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("div", { ref: triggerRef, children: _jsx(CalendarInput, { placeholder: placeholder, value: value, disabled: disabled, state: state, className: inputClassName, iconBGFull: iconBGFull, iconPosition: iconPosition, showChevron: showChevron, isOpen: isOpen, icon: icon }) }) }), _jsx(PopoverContent, { ref: popoverContentRef, className: "w-auto border-transparent !p-0 transfer-calendar", side: "bottom", align: "start", sideOffset: 8, avoidCollisions: false, children: _jsx(DateTime, { mode: mode, selectionMode: selectionMode, numberOfMonths: numberOfMonths, disableBeforeToday: disableBeforeToday, disableToday: disableToday, onChange: handleDtChange, defaultDateRange: selectedDateRange, defaultTime: selectedTime }) })] }));
|
|
113
113
|
};
|
|
114
114
|
export default DateTimePicker;
|
|
@@ -40,5 +40,5 @@ export function DialogDeleteConfirmMultiple(props) {
|
|
|
40
40
|
useEffect(function () {
|
|
41
41
|
setIsSelectAll(selectedQuotes.length === quotes.length);
|
|
42
42
|
}, [selectedQuotes, quotes]);
|
|
43
|
-
return (_jsx(DialogDeleteConfirm.Wrapper, { onCancel: onCancel, onOk: handleDeleteQuotes, children: _jsxs("div", { className: "confirm-multiple", children: [_jsx(Checkbox, { label: "Select all quotations", checked: isSelectAll, onChange: handleSelectAllQuotes, labelPosition: "leading", className: "confirm-multiple__all" }), _jsx("ul", { className: "mb-1", children: quotes.map(function (quote) { return (_jsx("li", { children: _jsx(Checkbox, { label: quote.name, checked: selectedQuotes.includes(quote.id), onChange: function () { return handleSelectQuote(quote); }, labelPosition: "leading" }) }, quote.id)); }) }), isSelectAll && (_jsx(Toast, { text: "Caution: This would delete all quotations created.", type: "warning", showIcon: false }))] }) }));
|
|
43
|
+
return (_jsx(DialogDeleteConfirm.Wrapper, { onCancel: onCancel, onOk: handleDeleteQuotes, okDisabled: selectedQuotes.length === 0, children: _jsxs("div", { className: "confirm-multiple", children: [_jsx(Checkbox, { label: "Select all quotations", checked: isSelectAll, onChange: handleSelectAllQuotes, labelPosition: "leading", className: "confirm-multiple__all" }), _jsx("ul", { className: "mb-1", children: quotes.map(function (quote) { return (_jsx("li", { children: _jsx(Checkbox, { label: quote.name, checked: selectedQuotes.includes(quote.id), onChange: function () { return handleSelectQuote(quote); }, labelPosition: "leading" }) }, quote.id)); }) }), isSelectAll && (_jsx(Toast, { text: "Caution: This would delete all quotations created.", type: "warning", showIcon: false }))] }) }));
|
|
44
44
|
}
|
|
@@ -6,5 +6,6 @@ export interface DialogDeleteConfirmWrapperProps {
|
|
|
6
6
|
onOk?: () => void;
|
|
7
7
|
okText?: string;
|
|
8
8
|
children: React.ReactNode;
|
|
9
|
+
okDisabled?: boolean;
|
|
9
10
|
}
|
|
10
11
|
export declare function DialogDeleteConfirmWrapper(props: DialogDeleteConfirmWrapperProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Button from '../../atoms/Button/Button';
|
|
3
3
|
export function DialogDeleteConfirmWrapper(props) {
|
|
4
|
-
var onCancel = props.onCancel, _a = props.cancelText, cancelText = _a === void 0 ? 'Cancel' : _a, _b = props.okVariant, okVariant = _b === void 0 ? 'destructive' : _b, onOk = props.onOk, _c = props.okText, okText = _c === void 0 ? 'Delete' : _c, children = props.children;
|
|
5
|
-
return (_jsxs("div", { className: "space-y-9", children: [children, _jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [_jsx(Button, { variant: "outline-secondary", onClick: onCancel, size: "sm", children: cancelText }), _jsx(Button, { variant: okVariant, onClick: onOk, size: "sm", children: okText })] })] }));
|
|
4
|
+
var onCancel = props.onCancel, _a = props.cancelText, cancelText = _a === void 0 ? 'Cancel' : _a, _b = props.okVariant, okVariant = _b === void 0 ? 'destructive' : _b, onOk = props.onOk, _c = props.okText, okText = _c === void 0 ? 'Delete' : _c, children = props.children, okDisabled = props.okDisabled;
|
|
5
|
+
return (_jsxs("div", { className: "space-y-9", children: [children, _jsxs("div", { className: "grid grid-cols-2 gap-x-4", children: [_jsx(Button, { variant: "outline-secondary", onClick: onCancel, size: "sm", children: cancelText }), _jsx(Button, { variant: okVariant, onClick: onOk, size: "sm", disabled: okDisabled, children: okText })] })] }));
|
|
6
6
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
3
|
import Button from '../../atoms/Button/Button';
|
|
4
4
|
import Input from '../../atoms/Inputs/Input/Input';
|
|
5
5
|
import { Text } from '../../atoms/Typography/Typography';
|
|
@@ -10,5 +10,8 @@ export function DialogQuoteRename(props) {
|
|
|
10
10
|
var handleQuoteNameChange = function (e) {
|
|
11
11
|
setValue(e.target.value);
|
|
12
12
|
};
|
|
13
|
-
|
|
13
|
+
useEffect(function () {
|
|
14
|
+
setValue(quoteName);
|
|
15
|
+
}, [quoteName]);
|
|
16
|
+
return (_jsx(DialogBookingConfirm, { open: open, setOpen: setOpen, title: "Rename Quotation", className: "!max-w-[500px]", children: _jsxs("div", { className: "space-y-9", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { htmlFor: "quote-name", children: _jsx(Text, { as: "span", size: "sm", children: "Quotation name" }) }), _jsx(Input, { id: "quote-name", value: value, onChange: handleQuoteNameChange, placeholder: "Enter your quote name" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-x-4 pb-2", children: [_jsx(Button, { variant: "outline-secondary", onClick: function () { return setOpen(false); }, size: "sm", children: "Cancel" }), _jsx(Button, { variant: "secondary", onClick: function () { return onSubmit(value); }, size: "sm", children: "Save changes" })] })] }) }));
|
|
14
17
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export type TransferMode = "roundtrip" | "custom";
|
|
1
|
+
import '../../../styles/components/organism/search-bar-transfer.css';
|
|
2
|
+
import { LocationGroup, LocationOption } from '../../molecules/LocationDropdown/LocationDropdown';
|
|
3
|
+
import { RoundTripData } from '../RoundTrip/RoundTrip';
|
|
4
|
+
import { TransferLineData } from '../TransferLine/TransferLine';
|
|
5
|
+
export type TransferMode = 'roundtrip' | 'custom';
|
|
7
6
|
export interface SearchBarTransferData {
|
|
8
7
|
mode: TransferMode;
|
|
9
8
|
sameVehicle: boolean;
|
|
@@ -35,5 +34,5 @@ export interface SearchBarTransferProps {
|
|
|
35
34
|
/** Whether to scroll to the input when the calendar opens */
|
|
36
35
|
scrollOnOpen?: boolean;
|
|
37
36
|
}
|
|
38
|
-
declare
|
|
37
|
+
declare function SearchBarTransfer({ mode: initialMode, locations, defaultRoundTripData, defaultTransferLines, defaultSameVehicle, onSearch, onChange, onRemove, className, scrollOnOpen, }: SearchBarTransferProps): import("react/jsx-runtime").JSX.Element;
|
|
39
38
|
export default SearchBarTransfer;
|
|
@@ -19,20 +19,20 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
19
19
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
20
20
|
};
|
|
21
21
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
22
|
-
import React, { useEffect, useState } from
|
|
23
|
-
import
|
|
24
|
-
import Button from
|
|
25
|
-
import Checkbox from
|
|
26
|
-
import Icon from
|
|
27
|
-
import SegmentedButton from
|
|
28
|
-
import { Heading, Text } from
|
|
29
|
-
import Toast from
|
|
30
|
-
import { DEFAULT_PAX_DATA_WITH_ADULTS } from
|
|
31
|
-
import RoundTrip from
|
|
32
|
-
import TransferLine from
|
|
22
|
+
import React, { useEffect, useState } from 'react';
|
|
23
|
+
import '../../../styles/components/organism/search-bar-transfer.css';
|
|
24
|
+
import Button from '../../atoms/Button/Button';
|
|
25
|
+
import Checkbox from '../../atoms/Checkbox/Checkbox';
|
|
26
|
+
import Icon from '../../atoms/Icon/Icon';
|
|
27
|
+
import SegmentedButton from '../../atoms/SegmentedButton/SegmentedButton';
|
|
28
|
+
import { Heading, Text } from '../../atoms/Typography/Typography';
|
|
29
|
+
import Toast from '../../molecules/Toast/Toast';
|
|
30
|
+
import { DEFAULT_PAX_DATA_WITH_ADULTS } from '../PaxSelector/PaxSelector';
|
|
31
|
+
import RoundTrip from '../RoundTrip/RoundTrip';
|
|
32
|
+
import TransferLine from '../TransferLine/TransferLine';
|
|
33
33
|
var transferIdCounter = 0;
|
|
34
|
-
|
|
35
|
-
var _b = _a.mode, initialMode = _b === void 0 ?
|
|
34
|
+
function SearchBarTransfer(_a) {
|
|
35
|
+
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
36
|
// Generate unique ID for transfer lines
|
|
37
37
|
var generateTransferId = function () {
|
|
38
38
|
transferIdCounter += 1;
|
|
@@ -55,15 +55,15 @@ var SearchBarTransfer = function (_a) {
|
|
|
55
55
|
var data = {
|
|
56
56
|
mode: mode,
|
|
57
57
|
sameVehicle: sameVehicle,
|
|
58
|
-
roundTripData: mode ===
|
|
59
|
-
transferLines: mode ===
|
|
58
|
+
roundTripData: mode === 'roundtrip' ? roundTripData : undefined,
|
|
59
|
+
transferLines: mode === 'custom' ? transferLines : undefined,
|
|
60
60
|
};
|
|
61
61
|
onChange === null || onChange === void 0 ? void 0 : onChange(data);
|
|
62
62
|
}, [mode, sameVehicle, roundTripData, transferLines, onChange]);
|
|
63
63
|
// Mode options for SegmentedButton
|
|
64
64
|
var modeOptions = [
|
|
65
|
-
{ value:
|
|
66
|
-
{ value:
|
|
65
|
+
{ value: 'roundtrip', label: 'Round trip' },
|
|
66
|
+
{ value: 'custom', label: 'Custom transfer' },
|
|
67
67
|
];
|
|
68
68
|
// Helper function to check if pax data is at default state (unchanged by user)
|
|
69
69
|
var isDefaultPaxData = function (paxData) {
|
|
@@ -107,7 +107,7 @@ var SearchBarTransfer = function (_a) {
|
|
|
107
107
|
// Create arrival transfer
|
|
108
108
|
var arrivalTransfer = {
|
|
109
109
|
id: generateTransferId(),
|
|
110
|
-
type:
|
|
110
|
+
type: 'arrival',
|
|
111
111
|
paxData: data.paxData,
|
|
112
112
|
transferDate: data.arrivalDate,
|
|
113
113
|
pickupPoint: data.pickupDropoffPoint,
|
|
@@ -117,7 +117,7 @@ var SearchBarTransfer = function (_a) {
|
|
|
117
117
|
// Create departure transfer
|
|
118
118
|
var departureTransfer = {
|
|
119
119
|
id: generateTransferId(),
|
|
120
|
-
type:
|
|
120
|
+
type: 'departure',
|
|
121
121
|
paxData: data.paxData,
|
|
122
122
|
transferDate: data.departureDate,
|
|
123
123
|
pickupPoint: data.accommodation,
|
|
@@ -131,7 +131,7 @@ var SearchBarTransfer = function (_a) {
|
|
|
131
131
|
var transferMode = newMode;
|
|
132
132
|
if (transferMode === mode)
|
|
133
133
|
return;
|
|
134
|
-
if (transferMode ===
|
|
134
|
+
if (transferMode === 'custom' && mode === 'roundtrip') {
|
|
135
135
|
// When switching from roundtrip to custom mode
|
|
136
136
|
if (hasRoundtripData(roundTripData)) {
|
|
137
137
|
// Half/full Roundtrip data → prefilled arrival and departure (2h)
|
|
@@ -144,7 +144,7 @@ var SearchBarTransfer = function (_a) {
|
|
|
144
144
|
}
|
|
145
145
|
setRoundTripData(undefined);
|
|
146
146
|
}
|
|
147
|
-
else if (transferMode ===
|
|
147
|
+
else if (transferMode === 'roundtrip' && mode === 'custom') {
|
|
148
148
|
// Reset everything when switching to roundtrip
|
|
149
149
|
setTransferLines([]);
|
|
150
150
|
setRoundTripData(undefined);
|
|
@@ -174,16 +174,16 @@ var SearchBarTransfer = function (_a) {
|
|
|
174
174
|
};
|
|
175
175
|
if (transferLines.length > 0) {
|
|
176
176
|
// If adding inter-hotel, set pickup point to the dropoff of latest arrival
|
|
177
|
-
if (type ===
|
|
178
|
-
var arrivalTransfers = transferLines.filter(function (line) { return line.type ===
|
|
177
|
+
if (type === 'inter-hotel') {
|
|
178
|
+
var arrivalTransfers = transferLines.filter(function (line) { return line.type === 'arrival'; });
|
|
179
179
|
if (arrivalTransfers.length > 0) {
|
|
180
180
|
var latestArrival = arrivalTransfers[arrivalTransfers.length - 1];
|
|
181
181
|
newTransfer.pickupPoint = latestArrival.dropoffPoint;
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
// If adding departure, set pickup point to the dropoff of latest arrival or inter-hotel
|
|
185
|
-
if (type ===
|
|
186
|
-
var arrivalAndInterHotelTransfers = transferLines.filter(function (line) { return line.type ===
|
|
185
|
+
if (type === 'departure') {
|
|
186
|
+
var arrivalAndInterHotelTransfers = transferLines.filter(function (line) { return line.type === 'arrival' || line.type === 'inter-hotel'; });
|
|
187
187
|
if (arrivalAndInterHotelTransfers.length > 0) {
|
|
188
188
|
var latestTransfer = arrivalAndInterHotelTransfers[arrivalAndInterHotelTransfers.length - 1];
|
|
189
189
|
newTransfer.pickupPoint = latestTransfer.dropoffPoint;
|
|
@@ -193,7 +193,7 @@ var SearchBarTransfer = function (_a) {
|
|
|
193
193
|
setTransferLines(__spreadArray(__spreadArray([], transferLines, true), [newTransfer], false));
|
|
194
194
|
};
|
|
195
195
|
// Render transfer type buttons
|
|
196
|
-
var renderTransferTypeButtons = function (onClick) { return (_jsxs("div", { className: "search-bar-transfer__add-buttons", children: [_jsx(Button, { variant: "outline-secondary", size: "sm", leadingIcon: "plus", onClick: function () { return onClick(
|
|
196
|
+
var renderTransferTypeButtons = function (onClick) { return (_jsxs("div", { className: "search-bar-transfer__add-buttons", children: [_jsx(Button, { variant: "outline-secondary", size: "sm", leadingIcon: "plus", onClick: function () { return onClick('arrival'); }, children: "Add Arrival" }), _jsx(Button, { variant: "outline-secondary", size: "sm", leadingIcon: "plus", onClick: function () { return onClick('departure'); }, children: "Add Departure" }), _jsx(Button, { variant: "outline-secondary", size: "sm", leadingIcon: "plus", onClick: function () { return onClick('inter-hotel'); }, children: "Add Inter-Hotel" })] })); };
|
|
197
197
|
// Handle adding transfer from round-trip mode (switches to custom and adds new transfer)
|
|
198
198
|
var handleAddTransferFromRoundTrip = function (type) {
|
|
199
199
|
var newTransferLines = [];
|
|
@@ -206,22 +206,22 @@ var SearchBarTransfer = function (_a) {
|
|
|
206
206
|
type: type,
|
|
207
207
|
paxData: roundTripData.paxData,
|
|
208
208
|
// Use the appropriate date based on transfer type
|
|
209
|
-
transferDate: type ===
|
|
209
|
+
transferDate: type === 'arrival'
|
|
210
210
|
? roundTripData.arrivalDate
|
|
211
|
-
: type ===
|
|
211
|
+
: type === 'departure'
|
|
212
212
|
? roundTripData.departureDate
|
|
213
213
|
: undefined,
|
|
214
214
|
// Use appropriate pickup/dropoff points based on transfer type
|
|
215
|
-
pickupPoint: type ===
|
|
215
|
+
pickupPoint: type === 'arrival'
|
|
216
216
|
? roundTripData.pickupDropoffPoint
|
|
217
|
-
: type ===
|
|
217
|
+
: type === 'departure'
|
|
218
218
|
? roundTripData.accommodation
|
|
219
|
-
: type ===
|
|
219
|
+
: type === 'inter-hotel'
|
|
220
220
|
? roundTripData.accommodation // Inter-hotel pickup = arrival's dropoff (accommodation)
|
|
221
221
|
: undefined,
|
|
222
|
-
dropoffPoint: type ===
|
|
222
|
+
dropoffPoint: type === 'arrival'
|
|
223
223
|
? roundTripData.accommodation
|
|
224
|
-
: type ===
|
|
224
|
+
: type === 'departure'
|
|
225
225
|
? roundTripData.pickupDropoffPoint
|
|
226
226
|
: undefined,
|
|
227
227
|
};
|
|
@@ -234,22 +234,22 @@ var SearchBarTransfer = function (_a) {
|
|
|
234
234
|
type: type,
|
|
235
235
|
paxData: roundTripData.paxData,
|
|
236
236
|
// Use the appropriate date based on transfer type
|
|
237
|
-
transferDate: type ===
|
|
237
|
+
transferDate: type === 'arrival'
|
|
238
238
|
? roundTripData.arrivalDate
|
|
239
|
-
: type ===
|
|
239
|
+
: type === 'departure'
|
|
240
240
|
? roundTripData.departureDate
|
|
241
241
|
: undefined,
|
|
242
242
|
// Use appropriate pickup/dropoff points based on transfer type
|
|
243
|
-
pickupPoint: type ===
|
|
243
|
+
pickupPoint: type === 'arrival'
|
|
244
244
|
? roundTripData.pickupDropoffPoint
|
|
245
|
-
: type ===
|
|
245
|
+
: type === 'departure'
|
|
246
246
|
? roundTripData.accommodation
|
|
247
|
-
: type ===
|
|
247
|
+
: type === 'inter-hotel'
|
|
248
248
|
? roundTripData.accommodation // Inter-hotel pickup = arrival's dropoff (accommodation)
|
|
249
249
|
: undefined,
|
|
250
|
-
dropoffPoint: type ===
|
|
250
|
+
dropoffPoint: type === 'arrival'
|
|
251
251
|
? roundTripData.accommodation
|
|
252
|
-
: type ===
|
|
252
|
+
: type === 'departure'
|
|
253
253
|
? roundTripData.pickupDropoffPoint
|
|
254
254
|
: undefined,
|
|
255
255
|
};
|
|
@@ -266,7 +266,7 @@ var SearchBarTransfer = function (_a) {
|
|
|
266
266
|
// Set the transfers and switch to custom mode
|
|
267
267
|
setTransferLines(newTransferLines);
|
|
268
268
|
setRoundTripData(undefined);
|
|
269
|
-
setMode(
|
|
269
|
+
setMode('custom');
|
|
270
270
|
setError(null);
|
|
271
271
|
};
|
|
272
272
|
// Handle transfer line data change
|
|
@@ -320,9 +320,9 @@ var SearchBarTransfer = function (_a) {
|
|
|
320
320
|
};
|
|
321
321
|
// Validate data
|
|
322
322
|
var validateData = function () {
|
|
323
|
-
if (mode ===
|
|
323
|
+
if (mode === 'roundtrip') {
|
|
324
324
|
if (!roundTripData) {
|
|
325
|
-
setError(
|
|
325
|
+
setError('Please fill in all the transfer details.');
|
|
326
326
|
return false;
|
|
327
327
|
}
|
|
328
328
|
// Check if required fields are filled
|
|
@@ -330,18 +330,18 @@ var SearchBarTransfer = function (_a) {
|
|
|
330
330
|
roundTripData.arrivalDate === roundTripData.departureDate ||
|
|
331
331
|
!roundTripData.pickupDropoffPoint ||
|
|
332
332
|
!roundTripData.accommodation) {
|
|
333
|
-
setError(
|
|
333
|
+
setError('Please fill in all the transfer details.');
|
|
334
334
|
return false;
|
|
335
335
|
}
|
|
336
336
|
// Check if all ages are filled
|
|
337
337
|
if (!areAllAgesFilled(roundTripData.paxData)) {
|
|
338
|
-
setError(
|
|
338
|
+
setError('Please fill in all the transfer details.');
|
|
339
339
|
return false;
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
342
|
else {
|
|
343
343
|
if (transferLines.length === 0) {
|
|
344
|
-
setError(
|
|
344
|
+
setError('Please add at least one transfer.');
|
|
345
345
|
return false;
|
|
346
346
|
}
|
|
347
347
|
// Check if all transfer lines have required data
|
|
@@ -351,12 +351,12 @@ var SearchBarTransfer = function (_a) {
|
|
|
351
351
|
!line.transferDate ||
|
|
352
352
|
!line.pickupPoint ||
|
|
353
353
|
!line.dropoffPoint) {
|
|
354
|
-
setError(
|
|
354
|
+
setError('Please fill in all the transfer details.');
|
|
355
355
|
return false;
|
|
356
356
|
}
|
|
357
357
|
// Check if all ages are filled for this line
|
|
358
358
|
if (!areAllAgesFilled(line.paxData)) {
|
|
359
|
-
setError(
|
|
359
|
+
setError('Please fill in all the transfer details.');
|
|
360
360
|
return false;
|
|
361
361
|
}
|
|
362
362
|
}
|
|
@@ -371,32 +371,32 @@ var SearchBarTransfer = function (_a) {
|
|
|
371
371
|
var data = {
|
|
372
372
|
mode: mode,
|
|
373
373
|
sameVehicle: sameVehicle,
|
|
374
|
-
roundTripData: mode ===
|
|
375
|
-
transferLines: mode ===
|
|
374
|
+
roundTripData: mode === 'roundtrip' ? roundTripData : undefined,
|
|
375
|
+
transferLines: mode === 'custom' ? transferLines : undefined,
|
|
376
376
|
};
|
|
377
377
|
onSearch === null || onSearch === void 0 ? void 0 : onSearch(data);
|
|
378
378
|
}
|
|
379
379
|
};
|
|
380
|
-
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 ===
|
|
380
|
+
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 }, "roundtrip-".concat(validationTrigger)), _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 () {
|
|
381
381
|
// Group transfers by type
|
|
382
382
|
var groupedTransfers = {
|
|
383
383
|
arrival: [],
|
|
384
384
|
departure: [],
|
|
385
|
-
|
|
385
|
+
'inter-hotel': [],
|
|
386
386
|
};
|
|
387
387
|
transferLines.forEach(function (line) {
|
|
388
388
|
groupedTransfers[line.type].push(line);
|
|
389
389
|
});
|
|
390
390
|
// Get the order of non-empty groups
|
|
391
|
-
var typeOrder = [
|
|
391
|
+
var typeOrder = ['arrival', 'inter-hotel', 'departure'];
|
|
392
392
|
var nonEmptyGroups = typeOrder.filter(function (type) { return groupedTransfers[type].length > 0; });
|
|
393
393
|
// Category labels and icons
|
|
394
394
|
var categoryInfo = {
|
|
395
|
-
arrival: { label:
|
|
396
|
-
departure: { label:
|
|
397
|
-
|
|
395
|
+
arrival: { label: 'Arrival', icon: 'arrival' },
|
|
396
|
+
departure: { label: 'Departure', icon: 'departure' },
|
|
397
|
+
'inter-hotel': { label: 'Inter-Hotel', icon: 'building' },
|
|
398
398
|
};
|
|
399
399
|
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 }, "".concat(line.id, "-").concat(validationTrigger))); }), 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)); });
|
|
400
|
-
})() }))] })), _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 &&
|
|
401
|
-
}
|
|
400
|
+
})() }))] })), _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" })] })] })] }));
|
|
401
|
+
}
|
|
402
402
|
export default SearchBarTransfer;
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { HTMLInputTypeAttribute } from 'react';
|
|
1
|
+
import React, { HTMLInputTypeAttribute, useEffect, useRef } from 'react';
|
|
2
2
|
import Icon, { IconName } from '../../Icon/Icon';
|
|
3
3
|
|
|
4
4
|
export interface InputProps {
|
|
@@ -30,6 +30,8 @@ const Input: React.FC<InputProps> = ({
|
|
|
30
30
|
type = 'text',
|
|
31
31
|
id,
|
|
32
32
|
}) => {
|
|
33
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
34
|
+
|
|
33
35
|
const baseClasses = 'input-field';
|
|
34
36
|
const variantClasses = {
|
|
35
37
|
default: 'input-field--default',
|
|
@@ -42,6 +44,12 @@ const Input: React.FC<InputProps> = ({
|
|
|
42
44
|
const inputClasses =
|
|
43
45
|
`${baseClasses} ${variantClasses[variant]} ${icon ? `input-field--with-icon input-field--icon-${iconPosition}` : ''} ${className}`.trim();
|
|
44
46
|
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
inputRef.current?.focus();
|
|
50
|
+
}, 0);
|
|
51
|
+
}, [value]);
|
|
52
|
+
|
|
45
53
|
return (
|
|
46
54
|
<div className={`input-wrapper ${icon ? 'input-wrapper--with-icon' : ''}`.trim()}>
|
|
47
55
|
{icon && iconPosition === 'leading' && (
|
|
@@ -50,6 +58,7 @@ const Input: React.FC<InputProps> = ({
|
|
|
50
58
|
</span>
|
|
51
59
|
)}
|
|
52
60
|
<input
|
|
61
|
+
ref={inputRef}
|
|
53
62
|
id={id}
|
|
54
63
|
type={type}
|
|
55
64
|
className={inputClasses}
|
|
@@ -181,9 +181,9 @@ const DateTimePicker: React.FC<DateTimePickerProps> = ({
|
|
|
181
181
|
</div>
|
|
182
182
|
</PopoverTrigger>
|
|
183
183
|
|
|
184
|
-
<PopoverContent
|
|
184
|
+
<PopoverContent
|
|
185
185
|
ref={popoverContentRef}
|
|
186
|
-
className="w-auto border-transparent !p-0"
|
|
186
|
+
className="w-auto border-transparent !p-0 transfer-calendar"
|
|
187
187
|
side="bottom"
|
|
188
188
|
align="start"
|
|
189
189
|
sideOffset={8}
|
|
@@ -48,7 +48,10 @@ export function DialogDeleteConfirmMultiple(props: DialogDeleteConfirmMultiplePr
|
|
|
48
48
|
}, [selectedQuotes, quotes]);
|
|
49
49
|
|
|
50
50
|
return (
|
|
51
|
-
<DialogDeleteConfirm.Wrapper
|
|
51
|
+
<DialogDeleteConfirm.Wrapper
|
|
52
|
+
onCancel={onCancel}
|
|
53
|
+
onOk={handleDeleteQuotes}
|
|
54
|
+
okDisabled={selectedQuotes.length === 0}>
|
|
52
55
|
<div className="confirm-multiple">
|
|
53
56
|
<Checkbox
|
|
54
57
|
label="Select all quotations"
|
|
@@ -7,6 +7,7 @@ export interface DialogDeleteConfirmWrapperProps {
|
|
|
7
7
|
onOk?: () => void;
|
|
8
8
|
okText?: string;
|
|
9
9
|
children: React.ReactNode;
|
|
10
|
+
okDisabled?: boolean;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export function DialogDeleteConfirmWrapper(props: DialogDeleteConfirmWrapperProps) {
|
|
@@ -17,6 +18,7 @@ export function DialogDeleteConfirmWrapper(props: DialogDeleteConfirmWrapperProp
|
|
|
17
18
|
onOk,
|
|
18
19
|
okText = 'Delete',
|
|
19
20
|
children,
|
|
21
|
+
okDisabled,
|
|
20
22
|
} = props;
|
|
21
23
|
return (
|
|
22
24
|
<div className="space-y-9">
|
|
@@ -25,7 +27,7 @@ export function DialogDeleteConfirmWrapper(props: DialogDeleteConfirmWrapperProp
|
|
|
25
27
|
<Button variant="outline-secondary" onClick={onCancel} size="sm">
|
|
26
28
|
{cancelText}
|
|
27
29
|
</Button>
|
|
28
|
-
<Button variant={okVariant} onClick={onOk} size="sm">
|
|
30
|
+
<Button variant={okVariant} onClick={onOk} size="sm" disabled={okDisabled}>
|
|
29
31
|
{okText}
|
|
30
32
|
</Button>
|
|
31
33
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import Button from '../../atoms/Button/Button';
|
|
3
3
|
import Input from '../../atoms/Inputs/Input/Input';
|
|
4
4
|
import { Text } from '../../atoms/Typography/Typography';
|
|
@@ -19,6 +19,10 @@ export function DialogQuoteRename(props: DialogQuoteRenameProps) {
|
|
|
19
19
|
setValue(e.target.value);
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
setValue(quoteName);
|
|
24
|
+
}, [quoteName]);
|
|
25
|
+
|
|
22
26
|
return (
|
|
23
27
|
<DialogBookingConfirm
|
|
24
28
|
open={open}
|
|
@@ -32,7 +36,12 @@ export function DialogQuoteRename(props: DialogQuoteRenameProps) {
|
|
|
32
36
|
Quotation name
|
|
33
37
|
</Text>
|
|
34
38
|
</label>
|
|
35
|
-
<Input
|
|
39
|
+
<Input
|
|
40
|
+
id="quote-name"
|
|
41
|
+
value={value}
|
|
42
|
+
onChange={handleQuoteNameChange}
|
|
43
|
+
placeholder="Enter your quote name"
|
|
44
|
+
/>
|
|
36
45
|
</div>
|
|
37
46
|
<div className="grid grid-cols-2 gap-x-4 pb-2">
|
|
38
47
|
<Button variant="outline-secondary" onClick={() => setOpen(false)} size="sm">
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import React, { useEffect, useState } from
|
|
2
|
-
import
|
|
3
|
-
import Button from
|
|
4
|
-
import Checkbox from
|
|
5
|
-
import Icon, { IconName } from
|
|
6
|
-
import SegmentedButton, {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import '../../../styles/components/organism/search-bar-transfer.css';
|
|
3
|
+
import Button from '../../atoms/Button/Button';
|
|
4
|
+
import Checkbox from '../../atoms/Checkbox/Checkbox';
|
|
5
|
+
import Icon, { IconName } from '../../atoms/Icon/Icon';
|
|
6
|
+
import SegmentedButton, {
|
|
7
|
+
SegmentedButtonOption,
|
|
8
|
+
} from '../../atoms/SegmentedButton/SegmentedButton';
|
|
9
|
+
import { Heading, Text } from '../../atoms/Typography/Typography';
|
|
10
|
+
import {
|
|
11
|
+
LocationGroup,
|
|
12
|
+
LocationOption,
|
|
13
|
+
} from '../../molecules/LocationDropdown/LocationDropdown';
|
|
14
|
+
import Toast from '../../molecules/Toast/Toast';
|
|
15
|
+
import { DEFAULT_PAX_DATA_WITH_ADULTS } from '../PaxSelector/PaxSelector';
|
|
16
|
+
import RoundTrip, { RoundTripData } from '../RoundTrip/RoundTrip';
|
|
12
17
|
import TransferLine, {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from
|
|
18
|
+
TransferLineData,
|
|
19
|
+
TransferType,
|
|
20
|
+
} from '../TransferLine/TransferLine';
|
|
16
21
|
|
|
17
|
-
export type TransferMode =
|
|
22
|
+
export type TransferMode = 'roundtrip' | 'custom';
|
|
18
23
|
|
|
19
24
|
export interface SearchBarTransferData {
|
|
20
25
|
mode: TransferMode;
|
|
@@ -51,8 +56,8 @@ export interface SearchBarTransferProps {
|
|
|
51
56
|
|
|
52
57
|
let transferIdCounter = 0;
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
mode: initialMode =
|
|
59
|
+
function SearchBarTransfer({
|
|
60
|
+
mode: initialMode = 'roundtrip',
|
|
56
61
|
locations = { options: [], groups: [] },
|
|
57
62
|
defaultRoundTripData,
|
|
58
63
|
defaultTransferLines,
|
|
@@ -60,9 +65,9 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
60
65
|
onSearch,
|
|
61
66
|
onChange,
|
|
62
67
|
onRemove,
|
|
63
|
-
className =
|
|
68
|
+
className = '',
|
|
64
69
|
scrollOnOpen = false,
|
|
65
|
-
})
|
|
70
|
+
}: SearchBarTransferProps) {
|
|
66
71
|
// Generate unique ID for transfer lines
|
|
67
72
|
const generateTransferId = () => {
|
|
68
73
|
transferIdCounter += 1;
|
|
@@ -80,7 +85,9 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
80
85
|
|
|
81
86
|
const [mode, setMode] = useState<TransferMode>(initialMode);
|
|
82
87
|
const [sameVehicle, setSameVehicle] = useState(defaultSameVehicle);
|
|
83
|
-
const [roundTripData, setRoundTripData] = useState<RoundTripData | undefined>(
|
|
88
|
+
const [roundTripData, setRoundTripData] = useState<RoundTripData | undefined>(
|
|
89
|
+
defaultRoundTripData
|
|
90
|
+
);
|
|
84
91
|
const [transferLines, setTransferLines] = useState<TransferLineData[]>(
|
|
85
92
|
initializeTransferLines(defaultTransferLines)
|
|
86
93
|
);
|
|
@@ -92,16 +99,16 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
92
99
|
const data: SearchBarTransferData = {
|
|
93
100
|
mode,
|
|
94
101
|
sameVehicle,
|
|
95
|
-
roundTripData: mode ===
|
|
96
|
-
transferLines: mode ===
|
|
102
|
+
roundTripData: mode === 'roundtrip' ? roundTripData : undefined,
|
|
103
|
+
transferLines: mode === 'custom' ? transferLines : undefined,
|
|
97
104
|
};
|
|
98
105
|
onChange?.(data);
|
|
99
106
|
}, [mode, sameVehicle, roundTripData, transferLines, onChange]);
|
|
100
107
|
|
|
101
108
|
// Mode options for SegmentedButton
|
|
102
109
|
const modeOptions: SegmentedButtonOption[] = [
|
|
103
|
-
{ value:
|
|
104
|
-
{ value:
|
|
110
|
+
{ value: 'roundtrip', label: 'Round trip' },
|
|
111
|
+
{ value: 'custom', label: 'Custom transfer' },
|
|
105
112
|
];
|
|
106
113
|
|
|
107
114
|
// Helper function to check if pax data is at default state (unchanged by user)
|
|
@@ -122,17 +129,20 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
122
129
|
// Check if pax data is not default (user actually changed it)
|
|
123
130
|
const hasNonDefaultPax = data.paxData && !isDefaultPaxData(data.paxData);
|
|
124
131
|
|
|
125
|
-
const airportDroppOffPoint = locations.options?.find(
|
|
132
|
+
const airportDroppOffPoint = locations.options?.find(
|
|
133
|
+
(option) => option.type === 'airport' || option.type === 'port'
|
|
134
|
+
);
|
|
126
135
|
// Check if pickupDropoffPoint has changed from default
|
|
127
|
-
const hasChangedPickupDropoff =
|
|
136
|
+
const hasChangedPickupDropoff =
|
|
137
|
+
data.pickupDropoffPoint?.id !== airportDroppOffPoint?.id;
|
|
128
138
|
|
|
129
139
|
return !!(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
140
|
+
hasNonDefaultPax ||
|
|
141
|
+
data.arrivalDate ||
|
|
142
|
+
data.departureDate ||
|
|
143
|
+
hasChangedPickupDropoff ||
|
|
144
|
+
data.accommodation
|
|
145
|
+
);
|
|
136
146
|
};
|
|
137
147
|
|
|
138
148
|
// Helper function to check if ALL roundtrip data fields are filled
|
|
@@ -154,7 +164,7 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
154
164
|
// Create arrival transfer
|
|
155
165
|
const arrivalTransfer: TransferLineData = {
|
|
156
166
|
id: generateTransferId(),
|
|
157
|
-
type:
|
|
167
|
+
type: 'arrival',
|
|
158
168
|
paxData: data.paxData,
|
|
159
169
|
transferDate: data.arrivalDate,
|
|
160
170
|
pickupPoint: data.pickupDropoffPoint,
|
|
@@ -165,7 +175,7 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
165
175
|
// Create departure transfer
|
|
166
176
|
const departureTransfer: TransferLineData = {
|
|
167
177
|
id: generateTransferId(),
|
|
168
|
-
type:
|
|
178
|
+
type: 'departure',
|
|
169
179
|
paxData: data.paxData,
|
|
170
180
|
transferDate: data.departureDate,
|
|
171
181
|
pickupPoint: data.accommodation,
|
|
@@ -181,7 +191,7 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
181
191
|
const transferMode = newMode as TransferMode;
|
|
182
192
|
if (transferMode === mode) return;
|
|
183
193
|
|
|
184
|
-
if (transferMode ===
|
|
194
|
+
if (transferMode === 'custom' && mode === 'roundtrip') {
|
|
185
195
|
// When switching from roundtrip to custom mode
|
|
186
196
|
if (hasRoundtripData(roundTripData)) {
|
|
187
197
|
// Half/full Roundtrip data → prefilled arrival and departure (2h)
|
|
@@ -192,7 +202,7 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
192
202
|
setTransferLines([]);
|
|
193
203
|
}
|
|
194
204
|
setRoundTripData(undefined);
|
|
195
|
-
} else if (transferMode ===
|
|
205
|
+
} else if (transferMode === 'roundtrip' && mode === 'custom') {
|
|
196
206
|
// Reset everything when switching to roundtrip
|
|
197
207
|
setTransferLines([]);
|
|
198
208
|
setRoundTripData(undefined);
|
|
@@ -205,7 +215,7 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
205
215
|
// Get the last inputted pax data from existing transfer lines
|
|
206
216
|
const getLastInputtedPaxData = () => {
|
|
207
217
|
// Find the last transfer line that has pax data
|
|
208
|
-
|
|
218
|
+
|
|
209
219
|
for (let i = transferLines.length - 1; i >= 0; i--) {
|
|
210
220
|
if (transferLines[i].paxData) {
|
|
211
221
|
return transferLines[i].paxData;
|
|
@@ -228,21 +238,22 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
228
238
|
|
|
229
239
|
if (transferLines.length > 0) {
|
|
230
240
|
// If adding inter-hotel, set pickup point to the dropoff of latest arrival
|
|
231
|
-
if (type ===
|
|
232
|
-
const arrivalTransfers = transferLines.filter(line => line.type ===
|
|
241
|
+
if (type === 'inter-hotel') {
|
|
242
|
+
const arrivalTransfers = transferLines.filter((line) => line.type === 'arrival');
|
|
233
243
|
if (arrivalTransfers.length > 0) {
|
|
234
244
|
const latestArrival = arrivalTransfers[arrivalTransfers.length - 1];
|
|
235
245
|
newTransfer.pickupPoint = latestArrival.dropoffPoint;
|
|
236
246
|
}
|
|
237
247
|
}
|
|
238
|
-
|
|
248
|
+
|
|
239
249
|
// If adding departure, set pickup point to the dropoff of latest arrival or inter-hotel
|
|
240
|
-
if (type ===
|
|
250
|
+
if (type === 'departure') {
|
|
241
251
|
const arrivalAndInterHotelTransfers = transferLines.filter(
|
|
242
|
-
line => line.type ===
|
|
252
|
+
(line) => line.type === 'arrival' || line.type === 'inter-hotel'
|
|
243
253
|
);
|
|
244
254
|
if (arrivalAndInterHotelTransfers.length > 0) {
|
|
245
|
-
const latestTransfer =
|
|
255
|
+
const latestTransfer =
|
|
256
|
+
arrivalAndInterHotelTransfers[arrivalAndInterHotelTransfers.length - 1];
|
|
246
257
|
newTransfer.pickupPoint = latestTransfer.dropoffPoint;
|
|
247
258
|
}
|
|
248
259
|
}
|
|
@@ -258,24 +269,21 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
258
269
|
variant="outline-secondary"
|
|
259
270
|
size="sm"
|
|
260
271
|
leadingIcon="plus"
|
|
261
|
-
onClick={() => onClick(
|
|
262
|
-
>
|
|
272
|
+
onClick={() => onClick('arrival')}>
|
|
263
273
|
Add Arrival
|
|
264
274
|
</Button>
|
|
265
275
|
<Button
|
|
266
276
|
variant="outline-secondary"
|
|
267
277
|
size="sm"
|
|
268
278
|
leadingIcon="plus"
|
|
269
|
-
onClick={() => onClick(
|
|
270
|
-
>
|
|
279
|
+
onClick={() => onClick('departure')}>
|
|
271
280
|
Add Departure
|
|
272
281
|
</Button>
|
|
273
282
|
<Button
|
|
274
283
|
variant="outline-secondary"
|
|
275
284
|
size="sm"
|
|
276
285
|
leadingIcon="plus"
|
|
277
|
-
onClick={() => onClick(
|
|
278
|
-
>
|
|
286
|
+
onClick={() => onClick('inter-hotel')}>
|
|
279
287
|
Add Inter-Hotel
|
|
280
288
|
</Button>
|
|
281
289
|
</div>
|
|
@@ -288,61 +296,67 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
288
296
|
if (isRoundtripDataComplete(roundTripData)) {
|
|
289
297
|
// Half/full Roundtrip data → prefilled arrival & departure + Adding the appropriate transfer type (2h)
|
|
290
298
|
const prefilledLines = createPrefilledTransferLines(roundTripData!);
|
|
291
|
-
|
|
299
|
+
|
|
292
300
|
// Add the new transfer of the clicked type with prefilled data
|
|
293
301
|
const newTransfer: TransferLineData = {
|
|
294
302
|
id: generateTransferId(),
|
|
295
303
|
type,
|
|
296
304
|
paxData: roundTripData!.paxData,
|
|
297
305
|
// Use the appropriate date based on transfer type
|
|
298
|
-
transferDate:
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
306
|
+
transferDate:
|
|
307
|
+
type === 'arrival'
|
|
308
|
+
? roundTripData!.arrivalDate
|
|
309
|
+
: type === 'departure'
|
|
310
|
+
? roundTripData!.departureDate
|
|
311
|
+
: undefined,
|
|
303
312
|
// Use appropriate pickup/dropoff points based on transfer type
|
|
304
|
-
pickupPoint:
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
313
|
+
pickupPoint:
|
|
314
|
+
type === 'arrival'
|
|
315
|
+
? roundTripData!.pickupDropoffPoint
|
|
316
|
+
: type === 'departure'
|
|
317
|
+
? roundTripData!.accommodation
|
|
318
|
+
: type === 'inter-hotel'
|
|
319
|
+
? roundTripData!.accommodation // Inter-hotel pickup = arrival's dropoff (accommodation)
|
|
320
|
+
: undefined,
|
|
321
|
+
dropoffPoint:
|
|
322
|
+
type === 'arrival'
|
|
323
|
+
? roundTripData!.accommodation
|
|
324
|
+
: type === 'departure'
|
|
325
|
+
? roundTripData!.pickupDropoffPoint
|
|
310
326
|
: undefined,
|
|
311
|
-
dropoffPoint: type === "arrival"
|
|
312
|
-
? roundTripData!.accommodation
|
|
313
|
-
: type === "departure"
|
|
314
|
-
? roundTripData!.pickupDropoffPoint
|
|
315
|
-
: undefined,
|
|
316
327
|
};
|
|
317
|
-
|
|
328
|
+
|
|
318
329
|
// Combine prefilled lines with the new transfer
|
|
319
330
|
newTransferLines = [...prefilledLines, newTransfer];
|
|
320
331
|
} else if (hasRoundtripData(roundTripData)) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
? roundTripData!.accommodation
|
|
341
|
-
: type === "departure"
|
|
342
|
-
? roundTripData!.pickupDropoffPoint
|
|
332
|
+
const newTransfer: TransferLineData = {
|
|
333
|
+
id: generateTransferId(),
|
|
334
|
+
type,
|
|
335
|
+
paxData: roundTripData!.paxData,
|
|
336
|
+
// Use the appropriate date based on transfer type
|
|
337
|
+
transferDate:
|
|
338
|
+
type === 'arrival'
|
|
339
|
+
? roundTripData!.arrivalDate
|
|
340
|
+
: type === 'departure'
|
|
341
|
+
? roundTripData!.departureDate
|
|
342
|
+
: undefined,
|
|
343
|
+
// Use appropriate pickup/dropoff points based on transfer type
|
|
344
|
+
pickupPoint:
|
|
345
|
+
type === 'arrival'
|
|
346
|
+
? roundTripData!.pickupDropoffPoint
|
|
347
|
+
: type === 'departure'
|
|
348
|
+
? roundTripData!.accommodation
|
|
349
|
+
: type === 'inter-hotel'
|
|
350
|
+
? roundTripData!.accommodation // Inter-hotel pickup = arrival's dropoff (accommodation)
|
|
343
351
|
: undefined,
|
|
344
|
-
|
|
345
|
-
|
|
352
|
+
dropoffPoint:
|
|
353
|
+
type === 'arrival'
|
|
354
|
+
? roundTripData!.accommodation
|
|
355
|
+
: type === 'departure'
|
|
356
|
+
? roundTripData!.pickupDropoffPoint
|
|
357
|
+
: undefined,
|
|
358
|
+
};
|
|
359
|
+
newTransferLines = [newTransfer];
|
|
346
360
|
} else {
|
|
347
361
|
// Empty Roundtrip data → Adding the appropriate transfer type (1h)
|
|
348
362
|
const newTransfer: TransferLineData = {
|
|
@@ -355,7 +369,7 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
355
369
|
// Set the transfers and switch to custom mode
|
|
356
370
|
setTransferLines(newTransferLines);
|
|
357
371
|
setRoundTripData(undefined);
|
|
358
|
-
setMode(
|
|
372
|
+
setMode('custom');
|
|
359
373
|
setError(null);
|
|
360
374
|
};
|
|
361
375
|
|
|
@@ -370,22 +384,22 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
370
384
|
const handleDeleteTransferLine = (id: string) => {
|
|
371
385
|
const index = transferLines.findIndex((line) => line.id === id);
|
|
372
386
|
const transferLine = transferLines.find((line) => line.id === id);
|
|
373
|
-
|
|
387
|
+
|
|
374
388
|
if (index !== -1 && transferLine) {
|
|
375
389
|
onRemove?.(index, transferLine);
|
|
376
390
|
}
|
|
377
|
-
|
|
391
|
+
|
|
378
392
|
setTransferLines((prevLines) => prevLines.filter((line) => line.id !== id));
|
|
379
393
|
};
|
|
380
394
|
|
|
381
395
|
// Helper function to check if all ages are filled for pax data
|
|
382
|
-
const areAllAgesFilled = (paxData: {
|
|
383
|
-
teens: number;
|
|
384
|
-
children: number;
|
|
385
|
-
infants: number;
|
|
386
|
-
teenAges?: number[];
|
|
387
|
-
childAges?: number[];
|
|
388
|
-
infantAges?: number[]
|
|
396
|
+
const areAllAgesFilled = (paxData: {
|
|
397
|
+
teens: number;
|
|
398
|
+
children: number;
|
|
399
|
+
infants: number;
|
|
400
|
+
teenAges?: number[];
|
|
401
|
+
childAges?: number[];
|
|
402
|
+
infantAges?: number[];
|
|
389
403
|
}): boolean => {
|
|
390
404
|
// Check teen ages
|
|
391
405
|
if (paxData.teens > 0) {
|
|
@@ -416,9 +430,9 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
416
430
|
|
|
417
431
|
// Validate data
|
|
418
432
|
const validateData = (): boolean => {
|
|
419
|
-
if (mode ===
|
|
433
|
+
if (mode === 'roundtrip') {
|
|
420
434
|
if (!roundTripData) {
|
|
421
|
-
setError(
|
|
435
|
+
setError('Please fill in all the transfer details.');
|
|
422
436
|
return false;
|
|
423
437
|
}
|
|
424
438
|
// Check if required fields are filled
|
|
@@ -428,17 +442,17 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
428
442
|
!roundTripData.pickupDropoffPoint ||
|
|
429
443
|
!roundTripData.accommodation
|
|
430
444
|
) {
|
|
431
|
-
setError(
|
|
445
|
+
setError('Please fill in all the transfer details.');
|
|
432
446
|
return false;
|
|
433
447
|
}
|
|
434
448
|
// Check if all ages are filled
|
|
435
449
|
if (!areAllAgesFilled(roundTripData.paxData)) {
|
|
436
|
-
setError(
|
|
450
|
+
setError('Please fill in all the transfer details.');
|
|
437
451
|
return false;
|
|
438
452
|
}
|
|
439
453
|
} else {
|
|
440
454
|
if (transferLines.length === 0) {
|
|
441
|
-
setError(
|
|
455
|
+
setError('Please add at least one transfer.');
|
|
442
456
|
return false;
|
|
443
457
|
}
|
|
444
458
|
// Check if all transfer lines have required data
|
|
@@ -449,12 +463,12 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
449
463
|
!line.pickupPoint ||
|
|
450
464
|
!line.dropoffPoint
|
|
451
465
|
) {
|
|
452
|
-
setError(
|
|
466
|
+
setError('Please fill in all the transfer details.');
|
|
453
467
|
return false;
|
|
454
468
|
}
|
|
455
469
|
// Check if all ages are filled for this line
|
|
456
470
|
if (!areAllAgesFilled(line.paxData)) {
|
|
457
|
-
setError(
|
|
471
|
+
setError('Please fill in all the transfer details.');
|
|
458
472
|
return false;
|
|
459
473
|
}
|
|
460
474
|
}
|
|
@@ -465,14 +479,14 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
465
479
|
|
|
466
480
|
// Handle search
|
|
467
481
|
const handleSearch = () => {
|
|
468
|
-
setValidationTrigger(prev => prev + 1);
|
|
469
|
-
|
|
482
|
+
setValidationTrigger((prev) => prev + 1);
|
|
483
|
+
|
|
470
484
|
if (validateData()) {
|
|
471
485
|
const data: SearchBarTransferData = {
|
|
472
486
|
mode,
|
|
473
487
|
sameVehicle,
|
|
474
|
-
roundTripData: mode ===
|
|
475
|
-
transferLines: mode ===
|
|
488
|
+
roundTripData: mode === 'roundtrip' ? roundTripData : undefined,
|
|
489
|
+
transferLines: mode === 'custom' ? transferLines : undefined,
|
|
476
490
|
};
|
|
477
491
|
onSearch?.(data);
|
|
478
492
|
}
|
|
@@ -498,7 +512,7 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
498
512
|
</div>
|
|
499
513
|
|
|
500
514
|
{/* Content based on mode */}
|
|
501
|
-
{mode ===
|
|
515
|
+
{mode === 'roundtrip' ? (
|
|
502
516
|
<>
|
|
503
517
|
<RoundTrip
|
|
504
518
|
key={`roundtrip-${validationTrigger}`}
|
|
@@ -522,7 +536,12 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
522
536
|
<>
|
|
523
537
|
{/* Transfer type selector */}
|
|
524
538
|
<div className="search-bar-transfer__transfer-type">
|
|
525
|
-
<Text
|
|
539
|
+
<Text
|
|
540
|
+
size="lg"
|
|
541
|
+
variant="bold"
|
|
542
|
+
color="subtle"
|
|
543
|
+
as="div"
|
|
544
|
+
className="search-bar-transfer__transfer-type-label">
|
|
526
545
|
Select a transfer type you want to add
|
|
527
546
|
</Text>
|
|
528
547
|
{renderTransferTypeButtons(handleAddTransfer)}
|
|
@@ -549,7 +568,7 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
549
568
|
const groupedTransfers: { [key in TransferType]: TransferLineData[] } = {
|
|
550
569
|
arrival: [],
|
|
551
570
|
departure: [],
|
|
552
|
-
|
|
571
|
+
'inter-hotel': [],
|
|
553
572
|
};
|
|
554
573
|
|
|
555
574
|
transferLines.forEach((line) => {
|
|
@@ -557,14 +576,18 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
557
576
|
});
|
|
558
577
|
|
|
559
578
|
// Get the order of non-empty groups
|
|
560
|
-
const typeOrder: TransferType[] = [
|
|
561
|
-
const nonEmptyGroups = typeOrder.filter(
|
|
579
|
+
const typeOrder: TransferType[] = ['arrival', 'inter-hotel', 'departure'];
|
|
580
|
+
const nonEmptyGroups = typeOrder.filter(
|
|
581
|
+
(type) => groupedTransfers[type].length > 0
|
|
582
|
+
);
|
|
562
583
|
|
|
563
584
|
// Category labels and icons
|
|
564
|
-
const categoryInfo: {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
585
|
+
const categoryInfo: {
|
|
586
|
+
[key in TransferType]: { label: string; icon: IconName };
|
|
587
|
+
} = {
|
|
588
|
+
arrival: { label: 'Arrival', icon: 'arrival' },
|
|
589
|
+
departure: { label: 'Departure', icon: 'departure' },
|
|
590
|
+
'inter-hotel': { label: 'Inter-Hotel', icon: 'building' },
|
|
568
591
|
};
|
|
569
592
|
|
|
570
593
|
return nonEmptyGroups.map((type, groupIndex) => (
|
|
@@ -598,19 +621,18 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
598
621
|
|
|
599
622
|
{/* Show separator between categories, but not after the last one */}
|
|
600
623
|
{groupIndex < nonEmptyGroups.length - 1 && (
|
|
601
|
-
<svg
|
|
602
|
-
className="search-bar-transfer__divider"
|
|
603
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
604
|
-
width="100%"
|
|
605
|
-
height="1"
|
|
606
|
-
viewBox="0 0 1076 1"
|
|
624
|
+
<svg
|
|
625
|
+
className="search-bar-transfer__divider"
|
|
626
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
627
|
+
width="100%"
|
|
628
|
+
height="1"
|
|
629
|
+
viewBox="0 0 1076 1"
|
|
607
630
|
fill="none"
|
|
608
|
-
preserveAspectRatio="none"
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
strokeWidth="1"
|
|
631
|
+
preserveAspectRatio="none">
|
|
632
|
+
<path
|
|
633
|
+
d="M0 0.5L1076 0.499906"
|
|
634
|
+
stroke="var(--color-border-medium, #A3A3A3)"
|
|
635
|
+
strokeWidth="1"
|
|
614
636
|
strokeDasharray="10 10"
|
|
615
637
|
/>
|
|
616
638
|
</svg>
|
|
@@ -632,23 +654,14 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
|
|
|
632
654
|
/>
|
|
633
655
|
|
|
634
656
|
<div className="search-bar-transfer__cta">
|
|
635
|
-
{error &&
|
|
636
|
-
|
|
637
|
-
text={error}
|
|
638
|
-
type="danger"
|
|
639
|
-
/>
|
|
640
|
-
)}
|
|
641
|
-
<Button
|
|
642
|
-
variant="primary"
|
|
643
|
-
size="lg"
|
|
644
|
-
onClick={handleSearch}
|
|
645
|
-
>
|
|
657
|
+
{error && <Toast text={error} type="danger" />}
|
|
658
|
+
<Button variant="primary" size="lg" onClick={handleSearch}>
|
|
646
659
|
Search
|
|
647
660
|
</Button>
|
|
648
661
|
</div>
|
|
649
662
|
</div>
|
|
650
663
|
</div>
|
|
651
664
|
);
|
|
652
|
-
}
|
|
665
|
+
}
|
|
653
666
|
|
|
654
667
|
export default SearchBarTransfer;
|