mautourco-components 0.2.63 → 0.2.65

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.
@@ -15,6 +15,8 @@ export interface AgeSelectorProps {
15
15
  className?: string;
16
16
  /** Placeholder text */
17
17
  placeholder?: string;
18
+ /** Whether to show error state */
19
+ error?: boolean;
18
20
  }
19
21
  declare const AgeSelector: React.FC<AgeSelectorProps>;
20
22
  export default AgeSelector;
@@ -4,9 +4,9 @@ import '../../../styles/components/molecule/age-selector.css';
4
4
  import Icon from '../../atoms/Icon/Icon';
5
5
  import { Text } from '../../atoms/Typography/Typography';
6
6
  var AgeSelector = function (_a) {
7
- var label = _a.label, value = _a.value, onChange = _a.onChange, ageRange = _a.ageRange, _b = _a.required, required = _b === void 0 ? false : _b, _c = _a.className, className = _c === void 0 ? '' : _c, _d = _a.placeholder, placeholder = _d === void 0 ? '--' : _d;
8
- var _e = useState(false), isOpen = _e[0], setIsOpen = _e[1];
9
- var _f = useState(value !== undefined ? value.toString() : ''), inputValue = _f[0], setInputValue = _f[1];
7
+ var label = _a.label, value = _a.value, onChange = _a.onChange, ageRange = _a.ageRange, _b = _a.required, required = _b === void 0 ? false : _b, _c = _a.className, className = _c === void 0 ? '' : _c, _d = _a.placeholder, placeholder = _d === void 0 ? '--' : _d, _e = _a.error, error = _e === void 0 ? false : _e;
8
+ var _f = useState(false), isOpen = _f[0], setIsOpen = _f[1];
9
+ var _g = useState(value !== undefined ? value.toString() : ''), inputValue = _g[0], setInputValue = _g[1];
10
10
  var containerRef = useRef(null);
11
11
  var inputRef = useRef(null);
12
12
  // Sync input value when prop value changes
@@ -75,7 +75,7 @@ var AgeSelector = function (_a) {
75
75
  setIsOpen(!isOpen);
76
76
  };
77
77
  var ageOptions = ageRange.map(function (age) { return age.toString(); });
78
- return (_jsxs("div", { className: "age-selector ".concat(className), ref: containerRef, children: [_jsxs(Text, { size: "sm", variant: "regular", className: "age-selector__label", children: [label, required && _jsx("span", { className: "age-selector__required", children: " *" })] }), _jsxs("div", { className: "age-selector__container", children: [_jsxs("div", { className: "age-selector__input ".concat(isOpen ? 'age-selector__input--open' : '', " ").concat(value !== undefined ? 'age-selector__input--selected' : 'age-selector__input--default'), children: [_jsx("input", { ref: inputRef, type: "text", inputMode: "numeric", className: "age-selector__input-field", value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleInputKeyDown, onFocus: function () { return setIsOpen(false); }, placeholder: placeholder, "aria-label": "".concat(label, " age") }), _jsx("button", { type: "button", className: "age-selector__dropdown-btn", onClick: function (e) {
78
+ return (_jsxs("div", { className: "age-selector ".concat(className), ref: containerRef, children: [_jsxs(Text, { size: "sm", variant: "regular", className: "age-selector__label", children: [label, required && _jsx("span", { className: "age-selector__required", children: " *" })] }), _jsxs("div", { className: "age-selector__container", children: [_jsxs("div", { className: "age-selector__input ".concat(isOpen ? 'age-selector__input--open' : '', " ").concat(value !== undefined ? 'age-selector__input--selected' : 'age-selector__input--default', " ").concat(error && value === undefined ? 'age-selector__input--error' : ''), children: [_jsx("input", { ref: inputRef, type: "text", inputMode: "numeric", className: "age-selector__input-field", value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, onKeyDown: handleInputKeyDown, onFocus: function () { return setIsOpen(false); }, placeholder: placeholder, "aria-label": "".concat(label, " age") }), _jsx("button", { type: "button", className: "age-selector__dropdown-btn", onClick: function (e) {
79
79
  e.preventDefault();
80
80
  e.stopPropagation();
81
81
  handleDropdownToggle();
@@ -64,6 +64,8 @@ export interface PaxSelectorProps {
64
64
  scrollOnOpen?: boolean;
65
65
  /** Age range for child categories */
66
66
  ageRange?: number[];
67
+ /** Whether to check if age inputs are empty and show error state */
68
+ checkEmpty?: boolean;
67
69
  }
68
70
  export declare const DEFAULT_PAX_DATA_WITH_ADULTS: PaxData;
69
71
  export declare const CHILD_CATEGORY_AGES: number[];
@@ -85,7 +85,7 @@ var ClientTypeSelector = function (_a) {
85
85
  return (_jsxs("div", { className: "pax-selector__client-type", children: [_jsx(Text, { size: "sm", variant: "regular", className: "pax-selector__client-type-label", children: "Client type" }), _jsxs("div", { className: "pax-selector__client-type-select", ref: containerRef, children: [_jsxs("button", { type: "button", className: "pax-selector__client-type-trigger", onClick: function () { return setIsOpen(!isOpen); }, "aria-expanded": isOpen, "aria-haspopup": "listbox", children: [_jsxs("div", { className: "pax-selector__client-type-content", children: [_jsx(Icon, { name: "user-icon", size: "sm", className: "pax-selector__client-type-icon" }), _jsx("span", { className: "pax-selector__client-type-text", children: value })] }), _jsx(Icon, { name: "chevron-down", size: "sm", className: "pax-selector__client-type-chevron ".concat(isOpen ? 'pax-selector__client-type-chevron--open' : '') })] }), isOpen && (_jsx("div", { className: "pax-selector__client-type-dropdown", role: "listbox", children: CLIENT_TYPES.map(function (type) { return (_jsxs("button", { type: "button", className: "pax-selector__client-type-option ".concat(value === type ? 'pax-selector__client-type-option--selected' : ''), onClick: function () { return handleSelect(type); }, role: "option", "aria-selected": value === type, children: [_jsx(Icon, { name: "user-icon", size: "sm" }), type] }, type)); }) }))] })] }));
86
86
  };
87
87
  var RoomEditor = function (_a) {
88
- var room = _a.room, roomNumber = _a.roomNumber, showRemove = _a.showRemove, maxAdults = _a.maxAdults, maxTeens = _a.maxTeens, maxChildren = _a.maxChildren, maxInfants = _a.maxInfants, onChange = _a.onChange, onRemove = _a.onRemove, scrollToRef = _a.scrollToRef, ageRange = _a.ageRange;
88
+ var room = _a.room, roomNumber = _a.roomNumber, showRemove = _a.showRemove, maxAdults = _a.maxAdults, maxTeens = _a.maxTeens, maxChildren = _a.maxChildren, maxInfants = _a.maxInfants, onChange = _a.onChange, onRemove = _a.onRemove, scrollToRef = _a.scrollToRef, ageRange = _a.ageRange, _b = _a.checkEmpty, checkEmpty = _b === void 0 ? false : _b;
89
89
  var roomAgesSectionRef = useRef(null);
90
90
  var previousRoomCounts = useRef({ teens: 0, children: 0, infants: 0 });
91
91
  var handleFieldChange = function (field, value) {
@@ -167,26 +167,26 @@ var RoomEditor = function (_a) {
167
167
  var actualIndex = chunkIndex * 2 + ageIndex;
168
168
  return (_jsx(AgeSelector, { label: "Teen", value: age, onChange: function (selectedAge) {
169
169
  return handleAgeChange('teenAges', actualIndex, selectedAge);
170
- }, ageRange: ageRange, required: true }, "teen-".concat(actualIndex)));
170
+ }, ageRange: ageRange, required: true, error: checkEmpty }, "teen-".concat(actualIndex)));
171
171
  }) }, "teen-chunk-".concat(chunkIndex))); }), room.children > 0 &&
172
172
  childAgeChunks.map(function (chunk, chunkIndex) { return (_jsx(Fragment, { children: chunk.map(function (age, ageIndex) {
173
173
  var actualIndex = chunkIndex * 2 + ageIndex;
174
174
  return (_jsx(AgeSelector, { label: "Child", value: age, onChange: function (selectedAge) {
175
175
  return handleAgeChange('childAges', actualIndex, selectedAge);
176
- }, ageRange: ageRange, required: true }, "child-".concat(actualIndex)));
176
+ }, ageRange: ageRange, required: true, error: checkEmpty }, "child-".concat(actualIndex)));
177
177
  }) }, "child-chunk-".concat(chunkIndex))); }), room.infants > 0 &&
178
178
  infantAgeChunks.map(function (chunk, chunkIndex) { return (_jsx(Fragment, { children: chunk.map(function (age, ageIndex) {
179
179
  var actualIndex = chunkIndex * 2 + ageIndex;
180
180
  return (_jsx(AgeSelector, { label: "Infant", value: age, onChange: function (selectedAge) {
181
181
  return handleAgeChange('infantAges', actualIndex, selectedAge);
182
- }, ageRange: ageRange, required: true }, "infant-".concat(actualIndex)));
182
+ }, ageRange: ageRange, required: true, error: checkEmpty }, "infant-".concat(actualIndex)));
183
183
  }) }, "infant-chunk-".concat(chunkIndex))); })] })] })), _jsx(ClientTypeSelector, { value: room.clientType, onChange: function (val) { return handleFieldChange('clientType', val); } })] })] }));
184
184
  };
185
185
  var PaxSelector = function (_a) {
186
- var _b = _a.label, label = _b === void 0 ? 'Number of pax' : _b, value = _a.value, onChange = _a.onChange, onAddRoom = _a.onAddRoom, onDone = _a.onDone, _c = _a.placeholder, placeholder = _c === void 0 ? 'Select pax' : _c, _d = _a.className, className = _d === void 0 ? '' : _d, _e = _a.maxAdults, maxAdults = _e === void 0 ? 10 : _e, _f = _a.maxTeens, maxTeens = _f === void 0 ? 10 : _f, _g = _a.maxChildren, maxChildren = _g === void 0 ? 10 : _g, _h = _a.maxInfants, maxInfants = _h === void 0 ? 10 : _h, _j = _a.showAddRoom, showAddRoom = _j === void 0 ? true : _j, _k = _a.multipleRooms, multipleRooms = _k === void 0 ? false : _k, defaultRooms = _a.defaultRooms, onRoomsChange = _a.onRoomsChange, onRemoveRoom = _a.onRemoveRoom, _l = _a.defaultPaxData, defaultPaxData = _l === void 0 ? DEFAULT_PAX_DATA_WITH_ADULTS : _l, _m = _a.ageRange, ageRange = _m === void 0 ? CHILD_CATEGORY_AGES : _m, _o = _a.scrollOnOpen, scrollOnOpen = _o === void 0 ? false : _o, _p = _a.disabled, disabled = _p === void 0 ? false : _p;
187
- var _q = useState(false), isOpen = _q[0], setIsOpen = _q[1];
188
- var _r = useState(value || defaultPaxData || DEFAULT_PAX_DATA), internalData = _r[0], setInternalData = _r[1];
189
- var _s = useState(defaultRooms || [__assign(__assign({}, DEFAULT_PAX_DATA), { roomId: '1' })]), rooms = _s[0], setRooms = _s[1];
186
+ var _b = _a.label, label = _b === void 0 ? 'Number of pax' : _b, value = _a.value, onChange = _a.onChange, onAddRoom = _a.onAddRoom, onDone = _a.onDone, _c = _a.placeholder, placeholder = _c === void 0 ? 'Select pax' : _c, _d = _a.className, className = _d === void 0 ? '' : _d, _e = _a.maxAdults, maxAdults = _e === void 0 ? 10 : _e, _f = _a.maxTeens, maxTeens = _f === void 0 ? 10 : _f, _g = _a.maxChildren, maxChildren = _g === void 0 ? 10 : _g, _h = _a.maxInfants, maxInfants = _h === void 0 ? 10 : _h, _j = _a.showAddRoom, showAddRoom = _j === void 0 ? true : _j, _k = _a.multipleRooms, multipleRooms = _k === void 0 ? false : _k, defaultRooms = _a.defaultRooms, onRoomsChange = _a.onRoomsChange, onRemoveRoom = _a.onRemoveRoom, _l = _a.defaultPaxData, defaultPaxData = _l === void 0 ? DEFAULT_PAX_DATA_WITH_ADULTS : _l, _m = _a.ageRange, ageRange = _m === void 0 ? CHILD_CATEGORY_AGES : _m, _o = _a.scrollOnOpen, scrollOnOpen = _o === void 0 ? false : _o, _p = _a.disabled, disabled = _p === void 0 ? false : _p, _q = _a.checkEmpty, checkEmpty = _q === void 0 ? false : _q;
187
+ var _r = useState(false), isOpen = _r[0], setIsOpen = _r[1];
188
+ var _s = useState(value || defaultPaxData || DEFAULT_PAX_DATA), internalData = _s[0], setInternalData = _s[1];
189
+ var _t = useState(defaultRooms || [__assign(__assign({}, DEFAULT_PAX_DATA), { roomId: '1' })]), rooms = _t[0], setRooms = _t[1];
190
190
  var containerRef = useRef(null);
191
191
  var triggerRef = useRef(null);
192
192
  var hasInitialized = useRef(false);
@@ -252,11 +252,76 @@ var PaxSelector = function (_a) {
252
252
  }
253
253
  // eslint-disable-next-line react-hooks/exhaustive-deps
254
254
  }, [internalData.teens, internalData.children, internalData.infants, multipleRooms]);
255
+ // Helper function to check if all ages are filled
256
+ var areAllAgesFilled = function () {
257
+ if (multipleRooms) {
258
+ // Check all rooms
259
+ for (var _i = 0, rooms_1 = rooms; _i < rooms_1.length; _i++) {
260
+ var room = rooms_1[_i];
261
+ if (room.teens > 0) {
262
+ var teenAges = room.teenAges || [];
263
+ for (var i = 0; i < room.teens; i++) {
264
+ if (teenAges[i] === undefined || teenAges[i] === null)
265
+ return false;
266
+ }
267
+ }
268
+ if (room.children > 0) {
269
+ var childAges = room.childAges || [];
270
+ for (var i = 0; i < room.children; i++) {
271
+ if (childAges[i] === undefined || childAges[i] === null)
272
+ return false;
273
+ }
274
+ }
275
+ if (room.infants > 0) {
276
+ var infantAges = room.infantAges || [];
277
+ for (var i = 0; i < room.infants; i++) {
278
+ if (infantAges[i] === undefined || infantAges[i] === null)
279
+ return false;
280
+ }
281
+ }
282
+ }
283
+ }
284
+ else {
285
+ // Check single room mode
286
+ if (internalData.teens > 0) {
287
+ var teenAges = internalData.teenAges || [];
288
+ for (var i = 0; i < internalData.teens; i++) {
289
+ if (teenAges[i] === undefined || teenAges[i] === null)
290
+ return false;
291
+ }
292
+ }
293
+ if (internalData.children > 0) {
294
+ var childAges = internalData.childAges || [];
295
+ for (var i = 0; i < internalData.children; i++) {
296
+ if (childAges[i] === undefined || childAges[i] === null)
297
+ return false;
298
+ }
299
+ }
300
+ if (internalData.infants > 0) {
301
+ var infantAges = internalData.infantAges || [];
302
+ for (var i = 0; i < internalData.infants; i++) {
303
+ if (infantAges[i] === undefined || infantAges[i] === null)
304
+ return false;
305
+ }
306
+ }
307
+ }
308
+ return true;
309
+ };
310
+ // Check if there are any minors that need age specification
311
+ var hasMinorsWithoutAges = function () {
312
+ if (multipleRooms) {
313
+ return rooms.some(function (room) { return room.teens > 0 || room.children > 0 || room.infants > 0; });
314
+ }
315
+ return internalData.teens > 0 || internalData.children > 0 || internalData.infants > 0;
316
+ };
255
317
  // Handle clicks outside the dropdown
256
318
  useEffect(function () {
257
319
  var handleClickOutside = function (event) {
258
320
  if (containerRef.current && !containerRef.current.contains(event.target)) {
259
- setIsOpen(false);
321
+ // Only close if all ages are filled (when there are minors)
322
+ if (!hasMinorsWithoutAges() || areAllAgesFilled()) {
323
+ setIsOpen(false);
324
+ }
260
325
  }
261
326
  };
262
327
  if (isOpen) {
@@ -265,7 +330,7 @@ var PaxSelector = function (_a) {
265
330
  return function () {
266
331
  document.removeEventListener('mousedown', handleClickOutside);
267
332
  };
268
- }, [isOpen]);
333
+ }, [isOpen, internalData, rooms, multipleRooms]);
269
334
  // Scroll to input when dropdown opens (if scrollOnOpen is true)
270
335
  useEffect(function () {
271
336
  scrollIntoViewOnOpen(triggerRef, isOpen, scrollOnOpen);
@@ -324,8 +389,11 @@ var PaxSelector = function (_a) {
324
389
  }
325
390
  };
326
391
  var handleDone = function () {
327
- setIsOpen(false);
328
- onDone === null || onDone === void 0 ? void 0 : onDone(multipleRooms ? rooms : internalData);
392
+ // Only close if all ages are filled (when there are minors)
393
+ if (!hasMinorsWithoutAges() || areAllAgesFilled()) {
394
+ setIsOpen(false);
395
+ onDone === null || onDone === void 0 ? void 0 : onDone(multipleRooms ? rooms : internalData);
396
+ }
329
397
  };
330
398
  var handleAddRoom = function () {
331
399
  var newRoom = __assign(__assign({}, DEFAULT_PAX_DATA), { roomId: "".concat(rooms.length + 1) });
@@ -372,7 +440,7 @@ var PaxSelector = function (_a) {
372
440
  return "".concat(total, " pax");
373
441
  };
374
442
  var hasPax = getTotalPax() > 0;
375
- return (_jsxs("div", { className: "pax-selector ".concat(disabled ? 'pax-selector--disabled' : '', " ").concat(className), ref: containerRef, children: [_jsxs("button", { ref: triggerRef, type: "button", className: "pax-selector__trigger", onClick: function () { return !disabled && setIsOpen(!isOpen); }, "aria-expanded": isOpen, "aria-haspopup": "true", disabled: disabled, children: [_jsx(Text, { size: "sm", variant: "regular", className: "pax-selector__label", children: label }), _jsxs("div", { className: "pax-selector__input ".concat(isOpen ? 'pax-selector__input--active' : '', " ").concat(disabled ? 'pax-selector__input--disabled' : ''), children: [_jsxs("div", { className: "pax-selector__input-content", children: [_jsx(Icon, { name: "user-icon", size: "sm", className: "pax-selector__input-icon" }), _jsx("span", { className: "pax-selector__input-text ".concat(!hasPax ? 'pax-selector__input-placeholder' : ''), children: getDisplayText() })] }), _jsx(Icon, { name: "chevron-down", size: "sm", className: "pax-selector__chevron ".concat(isOpen ? 'pax-selector__chevron--open' : '') })] })] }), isOpen && (_jsxs("div", { className: "pax-selector__dropdown", children: [multipleRooms ? (_jsxs(_Fragment, { children: [showAddRoom && (_jsxs("button", { type: "button", className: "pax-selector__add-room", onClick: handleAddRoom, children: [_jsx(Icon, { name: "home", size: "sm", className: "pax-selector__add-room-icon" }), "Add a room", _jsx(Icon, { name: "plus", size: "sm", className: "pax-selector__add-room-icon" })] })), _jsx("div", { className: "pax-selector__rooms", children: rooms.map(function (room, index) { return (_jsx(RoomEditor, { room: room, roomNumber: index + 1, showRemove: rooms.length > 1, maxAdults: maxAdults, maxTeens: maxTeens, maxChildren: maxChildren, maxInfants: maxInfants, onChange: function (updatedRoom) { return handleRoomChange(room.roomId, updatedRoom); }, onRemove: function () { return handleRemoveRoom(room.roomId); }, scrollToRef: index === rooms.length - 1 ? lastRoomRef : undefined, ageRange: ageRange }, room.roomId)); }) })] })) : (
443
+ return (_jsxs("div", { className: "pax-selector ".concat(disabled ? 'pax-selector--disabled' : '', " ").concat(className), ref: containerRef, children: [_jsxs("button", { ref: triggerRef, type: "button", className: "pax-selector__trigger", onClick: function () { return !disabled && setIsOpen(!isOpen); }, "aria-expanded": isOpen, "aria-haspopup": "true", disabled: disabled, children: [_jsx(Text, { size: "sm", variant: "regular", className: "pax-selector__label", children: label }), _jsxs("div", { className: "pax-selector__input ".concat(isOpen ? 'pax-selector__input--active' : '', " ").concat(disabled ? 'pax-selector__input--disabled' : ''), children: [_jsxs("div", { className: "pax-selector__input-content", children: [_jsx(Icon, { name: "user-icon", size: "sm", className: "pax-selector__input-icon" }), _jsx("span", { className: "pax-selector__input-text ".concat(!hasPax ? 'pax-selector__input-placeholder' : ''), children: getDisplayText() })] }), _jsx(Icon, { name: "chevron-down", size: "sm", className: "pax-selector__chevron ".concat(isOpen ? 'pax-selector__chevron--open' : '') })] })] }), isOpen && (_jsxs("div", { className: "pax-selector__dropdown", children: [multipleRooms ? (_jsxs(_Fragment, { children: [showAddRoom && (_jsxs("button", { type: "button", className: "pax-selector__add-room", onClick: handleAddRoom, children: [_jsx(Icon, { name: "home", size: "sm", className: "pax-selector__add-room-icon" }), "Add a room", _jsx(Icon, { name: "plus", size: "sm", className: "pax-selector__add-room-icon" })] })), _jsx("div", { className: "pax-selector__rooms", children: rooms.map(function (room, index) { return (_jsx(RoomEditor, { room: room, roomNumber: index + 1, showRemove: rooms.length > 1, maxAdults: maxAdults, maxTeens: maxTeens, maxChildren: maxChildren, maxInfants: maxInfants, onChange: function (updatedRoom) { return handleRoomChange(room.roomId, updatedRoom); }, onRemove: function () { return handleRemoveRoom(room.roomId); }, scrollToRef: index === rooms.length - 1 ? lastRoomRef : undefined, ageRange: ageRange, checkEmpty: checkEmpty }, room.roomId)); }) })] })) : (
376
444
  /* Single Room Mode */
377
445
  _jsxs(_Fragment, { children: [_jsxs("div", { className: "pax-selector__steppers", children: [_jsx(StepperRow, { label: "Adult", value: internalData.adults, max: maxAdults, onChange: function (val) { return handleDataChange('adults', val); } }), _jsx(StepperRow, { label: "Teen", value: internalData.teens, max: maxTeens, onChange: function (val) { return handleDataChange('teens', val); } }), _jsx(StepperRow, { label: "Child", value: internalData.children, max: maxChildren, onChange: function (val) { return handleDataChange('children', val); } }), _jsx(StepperRow, { label: "Infant", value: internalData.infants, max: maxInfants, onChange: function (val) { return handleDataChange('infants', val); } })] }), (internalData.teens > 0 ||
378
446
  internalData.children > 0 ||
@@ -397,19 +465,19 @@ var PaxSelector = function (_a) {
397
465
  var actualIndex = chunkIndex * 2 + ageIndex;
398
466
  return (_jsx(AgeSelector, { label: "Teen", value: age, onChange: function (selectedAge) {
399
467
  return handleAgeChange('teenAges', actualIndex, selectedAge);
400
- }, ageRange: ageRange, required: true }, "teen-".concat(actualIndex)));
468
+ }, ageRange: ageRange, required: true, error: checkEmpty }, "teen-".concat(actualIndex)));
401
469
  }) }, "teen-chunk-".concat(chunkIndex))); }), internalData.children > 0 &&
402
470
  childAgeChunks.map(function (chunk, chunkIndex) { return (_jsx(Fragment, { children: chunk.map(function (age, ageIndex) {
403
471
  var actualIndex = chunkIndex * 2 + ageIndex;
404
472
  return (_jsx(AgeSelector, { label: "Child", value: age, onChange: function (selectedAge) {
405
473
  return handleAgeChange('childAges', actualIndex, selectedAge);
406
- }, ageRange: ageRange, required: true }, "child-".concat(actualIndex)));
474
+ }, ageRange: ageRange, required: true, error: checkEmpty }, "child-".concat(actualIndex)));
407
475
  }) }, "child-chunk-".concat(chunkIndex))); }), internalData.infants > 0 &&
408
476
  infantAgeChunks.map(function (chunk, chunkIndex) { return (_jsx(Fragment, { children: chunk.map(function (age, ageIndex) {
409
477
  var actualIndex = chunkIndex * 2 + ageIndex;
410
478
  return (_jsx(AgeSelector, { label: "Infant", value: age, onChange: function (selectedAge) {
411
479
  return handleAgeChange('infantAges', actualIndex, selectedAge);
412
- }, ageRange: ageRange, required: true }, "infant-".concat(actualIndex)));
480
+ }, ageRange: ageRange, required: true, error: checkEmpty }, "infant-".concat(actualIndex)));
413
481
  }) }, "infant-chunk-".concat(chunkIndex))); })] }));
414
482
  })() })] })), _jsx(ClientTypeSelector, { value: internalData.clientType, onChange: function (val) { return handleDataChange('clientType', val); } })] })), _jsxs("div", { className: "pax-selector__actions", children: [_jsx("button", { type: "button", className: "pax-selector__clear-btn", onClick: handleClear, children: "Clear Pax" }), _jsx("button", { type: "button", className: "pax-selector__done-btn", onClick: handleDone, children: "Done" })] })] }))] }));
415
483
  };
@@ -184,6 +184,6 @@ var RoundTrip = function (_a) {
184
184
  var isAccommodationEmpty = checkEmpty && !internalAccommodation;
185
185
  var pickupDropoffOptions = getPickupDropoffOptions();
186
186
  var accommodationOptions = getAccommodationOptions();
187
- return (_jsx("div", { className: "round-trip ".concat(className), "data-round-trip-id": id, children: _jsxs("div", { className: "round-trip__content", children: [_jsx("div", { className: "round-trip__field round-trip__field--pax ".concat(isPaxEmpty ? 'round-trip__field--error' : ''), children: _jsx(PaxSelector, { label: "Number of pax", value: internalPaxData, onChange: handlePaxChange, placeholder: "2 pax", className: isPaxEmpty ? 'pax-selector--error' : '', scrollOnOpen: scrollOnOpen }) }), _jsxs("div", { className: "round-trip__field round-trip__field--dates", children: [_jsx(Text, { size: "sm", variant: "regular", className: "round-trip__field-label", children: "Arrival date - Departure date" }), _jsx(DateTimePicker, { placeholder: "DD/MM/YYYY - DD/MM/YYYY", mode: "calendar", iconPosition: "left", numberOfMonths: 2, iconBGFull: false, showChevron: true, onValueChange: handleDateRangeChange, selectionMode: "range", defaultValue: internalArrivalDate && internalDepartureDate ? [internalArrivalDate, internalDepartureDate] : undefined, inputClassName: "round-trip__date-picker--input", state: isDateEmpty ? 'error' : undefined, scrollOnOpen: scrollOnOpen })] }), _jsx("div", { className: "round-trip__field round-trip__field--pickup-dropoff", children: _jsx(LocationDropdown, { label: "Pick up / Drop-off point", options: pickupDropoffOptions.options, groups: pickupDropoffOptions.groups, selectedValue: (internalPickupDropoffPoint === null || internalPickupDropoffPoint === void 0 ? void 0 : internalPickupDropoffPoint.id) || null, onSelectionChange: handlePickupDropoffChange, placeholder: "Select pick-up / drop-off point", direction: undefined, type: "airport-port", showGroupTitles: true, error: isPickupDropoffEmpty, scrollOnOpen: scrollOnOpen }) }), _jsx("div", { className: "round-trip__field round-trip__field--accommodation", children: _jsx(LocationDropdown, { label: "Accommodation", options: accommodationOptions.options, groups: accommodationOptions.groups, selectedValue: (internalAccommodation === null || internalAccommodation === void 0 ? void 0 : internalAccommodation.id) || null, onSelectionChange: handleAccommodationChange, placeholder: "Select accommodation", direction: "dropoff", type: "accommodation", showGroupTitles: false, error: isAccommodationEmpty, scrollOnOpen: scrollOnOpen }) })] }) }));
187
+ return (_jsx("div", { className: "round-trip ".concat(className), "data-round-trip-id": id, children: _jsxs("div", { className: "round-trip__content", children: [_jsx("div", { className: "round-trip__field round-trip__field--pax ".concat(isPaxEmpty ? 'round-trip__field--error' : ''), children: _jsx(PaxSelector, { label: "Number of pax", value: internalPaxData, onChange: handlePaxChange, placeholder: "2 pax", className: isPaxEmpty ? 'pax-selector--error' : '', scrollOnOpen: scrollOnOpen, checkEmpty: checkEmpty }) }), _jsxs("div", { className: "round-trip__field round-trip__field--dates", children: [_jsx(Text, { size: "sm", variant: "regular", className: "round-trip__field-label", children: "Arrival date - Departure date" }), _jsx(DateTimePicker, { placeholder: "DD/MM/YYYY - DD/MM/YYYY", mode: "calendar", iconPosition: "left", numberOfMonths: 2, iconBGFull: false, showChevron: true, onValueChange: handleDateRangeChange, selectionMode: "range", defaultValue: internalArrivalDate && internalDepartureDate ? [internalArrivalDate, internalDepartureDate] : undefined, inputClassName: "round-trip__date-picker--input", state: isDateEmpty ? 'error' : undefined, scrollOnOpen: scrollOnOpen })] }), _jsx("div", { className: "round-trip__field round-trip__field--pickup-dropoff", children: _jsx(LocationDropdown, { label: "Pick up / Drop-off point", options: pickupDropoffOptions.options, groups: pickupDropoffOptions.groups, selectedValue: (internalPickupDropoffPoint === null || internalPickupDropoffPoint === void 0 ? void 0 : internalPickupDropoffPoint.id) || null, onSelectionChange: handlePickupDropoffChange, placeholder: "Select pick-up / drop-off point", direction: undefined, type: "airport-port", showGroupTitles: true, error: isPickupDropoffEmpty, scrollOnOpen: scrollOnOpen }) }), _jsx("div", { className: "round-trip__field round-trip__field--accommodation", children: _jsx(LocationDropdown, { label: "Accommodation", options: accommodationOptions.options, groups: accommodationOptions.groups, selectedValue: (internalAccommodation === null || internalAccommodation === void 0 ? void 0 : internalAccommodation.id) || null, onSelectionChange: handleAccommodationChange, placeholder: "Select accommodation", direction: "dropoff", type: "accommodation", showGroupTitles: false, error: isAccommodationEmpty, scrollOnOpen: scrollOnOpen }) })] }) }));
188
188
  };
189
189
  export default RoundTrip;
@@ -120,6 +120,40 @@ var SearchBarTransfer = function (_a) {
120
120
  }
121
121
  setTransferLines(function (prevLines) { return prevLines.filter(function (line) { return line.id !== id; }); });
122
122
  };
123
+ // Helper function to check if all ages are filled for pax data
124
+ var areAllAgesFilled = function (paxData) {
125
+ // Check teen ages
126
+ if (paxData.teens > 0) {
127
+ var teenAges = paxData.teenAges || [];
128
+ if (teenAges.length < paxData.teens)
129
+ return false;
130
+ for (var i = 0; i < paxData.teens; i++) {
131
+ if (teenAges[i] === undefined || teenAges[i] === null)
132
+ return false;
133
+ }
134
+ }
135
+ // Check child ages
136
+ if (paxData.children > 0) {
137
+ var childAges = paxData.childAges || [];
138
+ if (childAges.length < paxData.children)
139
+ return false;
140
+ for (var i = 0; i < paxData.children; i++) {
141
+ if (childAges[i] === undefined || childAges[i] === null)
142
+ return false;
143
+ }
144
+ }
145
+ // Check infant ages
146
+ if (paxData.infants > 0) {
147
+ var infantAges = paxData.infantAges || [];
148
+ if (infantAges.length < paxData.infants)
149
+ return false;
150
+ for (var i = 0; i < paxData.infants; i++) {
151
+ if (infantAges[i] === undefined || infantAges[i] === null)
152
+ return false;
153
+ }
154
+ }
155
+ return true;
156
+ };
123
157
  // Validate data
124
158
  var validateData = function () {
125
159
  if (mode === "roundtrip") {
@@ -136,6 +170,11 @@ var SearchBarTransfer = function (_a) {
136
170
  setError("Please fill in all the transfer details.");
137
171
  return false;
138
172
  }
173
+ // Check if all ages are filled
174
+ if (!areAllAgesFilled(roundTripData.paxData)) {
175
+ setError("Please fill in all the transfer details.");
176
+ return false;
177
+ }
139
178
  }
140
179
  else {
141
180
  if (transferLines.length === 0) {
@@ -152,6 +191,11 @@ var SearchBarTransfer = function (_a) {
152
191
  setError("Please fill in all the transfer details.");
153
192
  return false;
154
193
  }
194
+ // Check if all ages are filled for this line
195
+ if (!areAllAgesFilled(line.paxData)) {
196
+ setError("Please fill in all the transfer details.");
197
+ return false;
198
+ }
155
199
  }
156
200
  }
157
201
  setError(null);
@@ -192,7 +192,7 @@ var TransferLine = function (_a) {
192
192
  var isDateEmpty = checkEmpty && (!internalTransferDate || internalTransferDate === '');
193
193
  var isPickupEmpty = checkEmpty && !internalPickupPoint;
194
194
  var isDropoffEmpty = checkEmpty && !internalDropoffPoint;
195
- return (_jsxs("div", { className: "transfer-line transfer-line--".concat(type, " ").concat(disabled ? 'transfer-line--disabled' : '', " ").concat(className), "data-transfer-id": id, children: [showTitle && (_jsxs("div", { className: "transfer-line__header", children: [_jsx(Icon, { name: getTypeIcon(), size: "sm", className: "transfer-line__header-icon" }), _jsx(Text, { size: "sm", variant: "medium", className: "transfer-line__header-label", children: getTypeLabel() })] })), _jsxs("div", { className: "transfer-line__content-container", children: [_jsxs("div", { className: "transfer-line__content", children: [_jsx("div", { className: "transfer-line__field transfer-line__field--pax ".concat(isPaxEmpty ? 'transfer-line__field--error' : ''), children: _jsx(PaxSelector, { label: "Number of pax", value: internalPaxData, onChange: handlePaxChange, placeholder: "2 pax", className: isPaxEmpty ? 'pax-selector--error' : '', disabled: disabled, scrollOnOpen: scrollOnOpen }) }), _jsxs("div", { className: "transfer-line__field transfer-line__field--date", children: [_jsx(Text, { size: "sm", variant: "regular", className: "transfer-line__field-label", children: "Transfer date" }), _jsx(DateTimePicker, { placeholder: "DD/MM/YYYY", mode: "calendar", iconPosition: "left", numberOfMonths: 1, iconBGFull: false, showChevron: true, onValueChange: handleDateChange, selectionMode: "single", defaultValue: internalTransferDate, inputClassName: "transfer-line__date-picker", state: isDateEmpty ? 'error' : undefined, disabled: disabled, scrollOnOpen: scrollOnOpen })] }), _jsx("div", { className: "transfer-line__field transfer-line__field--pickup", children: _jsx(LocationDropdown, { label: "Pick-up point", options: filterLocations('pickup').options, groups: filterLocations('pickup').groups, selectedValue: (internalPickupPoint === null || internalPickupPoint === void 0 ? void 0 : internalPickupPoint.id) || null, onSelectionChange: handlePickupChange, placeholder: "Select a pick-up point", direction: "pickup", type: type === 'inter-hotel'
195
+ return (_jsxs("div", { className: "transfer-line transfer-line--".concat(type, " ").concat(disabled ? 'transfer-line--disabled' : '', " ").concat(className), "data-transfer-id": id, children: [showTitle && (_jsxs("div", { className: "transfer-line__header", children: [_jsx(Icon, { name: getTypeIcon(), size: "sm", className: "transfer-line__header-icon" }), _jsx(Text, { size: "sm", variant: "medium", className: "transfer-line__header-label", children: getTypeLabel() })] })), _jsxs("div", { className: "transfer-line__content-container", children: [_jsxs("div", { className: "transfer-line__content", children: [_jsx("div", { className: "transfer-line__field transfer-line__field--pax ".concat(isPaxEmpty ? 'transfer-line__field--error' : ''), children: _jsx(PaxSelector, { label: "Number of pax", value: internalPaxData, onChange: handlePaxChange, placeholder: "2 pax", className: isPaxEmpty ? 'pax-selector--error' : '', disabled: disabled, scrollOnOpen: scrollOnOpen, checkEmpty: checkEmpty }) }), _jsxs("div", { className: "transfer-line__field transfer-line__field--date", children: [_jsx(Text, { size: "sm", variant: "regular", className: "transfer-line__field-label", children: "Transfer date" }), _jsx(DateTimePicker, { placeholder: "DD/MM/YYYY", mode: "calendar", iconPosition: "left", numberOfMonths: 1, iconBGFull: false, showChevron: true, onValueChange: handleDateChange, selectionMode: "single", defaultValue: internalTransferDate, inputClassName: "transfer-line__date-picker", state: isDateEmpty ? 'error' : undefined, disabled: disabled, scrollOnOpen: scrollOnOpen })] }), _jsx("div", { className: "transfer-line__field transfer-line__field--pickup", children: _jsx(LocationDropdown, { label: "Pick-up point", options: filterLocations('pickup').options, groups: filterLocations('pickup').groups, selectedValue: (internalPickupPoint === null || internalPickupPoint === void 0 ? void 0 : internalPickupPoint.id) || null, onSelectionChange: handlePickupChange, placeholder: "Select a pick-up point", direction: "pickup", type: type === 'inter-hotel'
196
196
  ? 'accommodation'
197
197
  : type === 'arrival'
198
198
  ? 'airport-port'
package/dist/index.d.ts CHANGED
@@ -41,6 +41,7 @@ export { default as Stepper } from './components/molecules/Stepper/Stepper';
41
41
  export { default as TextWithIcon } from './components/molecules/TextWithIcon/TextWithIcon';
42
42
  export { default as TimelineItem } from './components/molecules/TimelineItem/TimelineItem';
43
43
  export { default as Toast } from './components/molecules/Toast/Toast';
44
+ export * from './components/molecules/TooltipDisplay/TooltipDisplay';
44
45
  export { Illustration } from './components/atoms/Illustration/Illustration';
45
46
  export * from './components/molecules/BookingPax';
46
47
  export { DetailsClientInfo } from './components/molecules/DetailsInfo/DetailsClient/DetailsClient';
package/dist/index.js CHANGED
@@ -43,6 +43,7 @@ export { default as Stepper } from './components/molecules/Stepper/Stepper';
43
43
  export { default as TextWithIcon } from './components/molecules/TextWithIcon/TextWithIcon';
44
44
  export { default as TimelineItem } from './components/molecules/TimelineItem/TimelineItem';
45
45
  export { default as Toast } from './components/molecules/Toast/Toast';
46
+ export * from './components/molecules/TooltipDisplay/TooltipDisplay';
46
47
  // Organisms - Complex components
47
48
  export { Illustration } from './components/atoms/Illustration/Illustration';
48
49
  export * from './components/molecules/BookingPax';
@@ -64,6 +64,27 @@
64
64
  border-color: var(--dropdown-color-border-hover,#0f7173);
65
65
  }
66
66
 
67
+ .age-selector__input--error {
68
+ border-color: var(--color-red-600,#dc2626);
69
+ background-color: var(--color-coral-red-50,#fff8f8);
70
+ }
71
+
72
+ .age-selector__input--error:hover {
73
+ border-color: var(--color-red-600,#dc2626);
74
+ }
75
+
76
+ .age-selector__input--error .age-selector__input-field {
77
+ color: var(--color-coral-red-600,#f83b3b);
78
+ }
79
+
80
+ .age-selector__input--error .age-selector__input-field::placeholder {
81
+ color: var(--color-coral-red-600,#f83b3b);
82
+ }
83
+
84
+ .age-selector__input--error .age-selector__icon {
85
+ color: var(--color-coral-red-600,#f83b3b);
86
+ }
87
+
67
88
  .age-selector__input--default .age-selector__input-field::placeholder {
68
89
  color: var(--dropdown-selector-color-filled-foreground-default,#737373);
69
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mautourco-components",
3
- "version": "0.2.63",
3
+ "version": "0.2.65",
4
4
  "private": false,
5
5
  "description": "Bibliothèque de composants Mautourco pour le redesign",
6
6
  "main": "dist/index.js",
@@ -18,6 +18,8 @@ export interface AgeSelectorProps {
18
18
  className?: string;
19
19
  /** Placeholder text */
20
20
  placeholder?: string;
21
+ /** Whether to show error state */
22
+ error?: boolean;
21
23
  }
22
24
 
23
25
  const AgeSelector: React.FC<AgeSelectorProps> = ({
@@ -28,6 +30,7 @@ const AgeSelector: React.FC<AgeSelectorProps> = ({
28
30
  required = false,
29
31
  className = '',
30
32
  placeholder = '--',
33
+ error = false,
31
34
  }) => {
32
35
  const [isOpen, setIsOpen] = useState(false);
33
36
  const [inputValue, setInputValue] = useState<string>(
@@ -118,7 +121,7 @@ const AgeSelector: React.FC<AgeSelectorProps> = ({
118
121
  </Text>
119
122
  <div className="age-selector__container">
120
123
  <div
121
- className={`age-selector__input ${isOpen ? 'age-selector__input--open' : ''} ${value !== undefined ? 'age-selector__input--selected' : 'age-selector__input--default'}`}>
124
+ className={`age-selector__input ${isOpen ? 'age-selector__input--open' : ''} ${value !== undefined ? 'age-selector__input--selected' : 'age-selector__input--default'} ${error && value === undefined ? 'age-selector__input--error' : ''}`}>
122
125
  <input
123
126
  ref={inputRef}
124
127
  type="text"
@@ -72,6 +72,8 @@ export interface PaxSelectorProps {
72
72
  scrollOnOpen?: boolean;
73
73
  /** Age range for child categories */
74
74
  ageRange?: number[];
75
+ /** Whether to check if age inputs are empty and show error state */
76
+ checkEmpty?: boolean;
75
77
  }
76
78
 
77
79
  const DEFAULT_PAX_DATA: PaxData = {
@@ -246,6 +248,7 @@ interface RoomEditorProps {
246
248
  onRemove: () => void;
247
249
  scrollToRef?: React.RefObject<HTMLDivElement | null>;
248
250
  ageRange: number[];
251
+ checkEmpty?: boolean;
249
252
  }
250
253
 
251
254
  const RoomEditor: React.FC<RoomEditorProps> = ({
@@ -260,6 +263,7 @@ const RoomEditor: React.FC<RoomEditorProps> = ({
260
263
  onRemove,
261
264
  scrollToRef,
262
265
  ageRange,
266
+ checkEmpty = false,
263
267
  }) => {
264
268
  const roomAgesSectionRef = useRef<HTMLDivElement>(null);
265
269
  const previousRoomCounts = useRef({ teens: 0, children: 0, infants: 0 });
@@ -444,6 +448,7 @@ const RoomEditor: React.FC<RoomEditorProps> = ({
444
448
  }
445
449
  ageRange={ageRange}
446
450
  required
451
+ error={checkEmpty}
447
452
  />
448
453
  );
449
454
  })}
@@ -466,6 +471,7 @@ const RoomEditor: React.FC<RoomEditorProps> = ({
466
471
  }
467
472
  ageRange={ageRange}
468
473
  required
474
+ error={checkEmpty}
469
475
  />
470
476
  );
471
477
  })}
@@ -488,6 +494,7 @@ const RoomEditor: React.FC<RoomEditorProps> = ({
488
494
  }
489
495
  ageRange={ageRange}
490
496
  required
497
+ error={checkEmpty}
491
498
  />
492
499
  );
493
500
  })}
@@ -527,6 +534,7 @@ const PaxSelector: React.FC<PaxSelectorProps> = ({
527
534
  ageRange = CHILD_CATEGORY_AGES,
528
535
  scrollOnOpen = false,
529
536
  disabled = false,
537
+ checkEmpty = false,
530
538
  }) => {
531
539
  const [isOpen, setIsOpen] = useState(false);
532
540
  const [internalData, setInternalData] = useState<PaxData>(
@@ -617,11 +625,70 @@ const PaxSelector: React.FC<PaxSelectorProps> = ({
617
625
  // eslint-disable-next-line react-hooks/exhaustive-deps
618
626
  }, [internalData.teens, internalData.children, internalData.infants, multipleRooms]);
619
627
 
628
+ // Helper function to check if all ages are filled
629
+ const areAllAgesFilled = (): boolean => {
630
+ if (multipleRooms) {
631
+ // Check all rooms
632
+ for (const room of rooms) {
633
+ if (room.teens > 0) {
634
+ const teenAges = room.teenAges || [];
635
+ for (let i = 0; i < room.teens; i++) {
636
+ if (teenAges[i] === undefined || teenAges[i] === null) return false;
637
+ }
638
+ }
639
+ if (room.children > 0) {
640
+ const childAges = room.childAges || [];
641
+ for (let i = 0; i < room.children; i++) {
642
+ if (childAges[i] === undefined || childAges[i] === null) return false;
643
+ }
644
+ }
645
+ if (room.infants > 0) {
646
+ const infantAges = room.infantAges || [];
647
+ for (let i = 0; i < room.infants; i++) {
648
+ if (infantAges[i] === undefined || infantAges[i] === null) return false;
649
+ }
650
+ }
651
+ }
652
+ } else {
653
+ // Check single room mode
654
+ if (internalData.teens > 0) {
655
+ const teenAges = internalData.teenAges || [];
656
+ for (let i = 0; i < internalData.teens; i++) {
657
+ if (teenAges[i] === undefined || teenAges[i] === null) return false;
658
+ }
659
+ }
660
+ if (internalData.children > 0) {
661
+ const childAges = internalData.childAges || [];
662
+ for (let i = 0; i < internalData.children; i++) {
663
+ if (childAges[i] === undefined || childAges[i] === null) return false;
664
+ }
665
+ }
666
+ if (internalData.infants > 0) {
667
+ const infantAges = internalData.infantAges || [];
668
+ for (let i = 0; i < internalData.infants; i++) {
669
+ if (infantAges[i] === undefined || infantAges[i] === null) return false;
670
+ }
671
+ }
672
+ }
673
+ return true;
674
+ };
675
+
676
+ // Check if there are any minors that need age specification
677
+ const hasMinorsWithoutAges = (): boolean => {
678
+ if (multipleRooms) {
679
+ return rooms.some(room => room.teens > 0 || room.children > 0 || room.infants > 0);
680
+ }
681
+ return internalData.teens > 0 || internalData.children > 0 || internalData.infants > 0;
682
+ };
683
+
620
684
  // Handle clicks outside the dropdown
621
685
  useEffect(() => {
622
686
  const handleClickOutside = (event: MouseEvent) => {
623
687
  if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
624
- setIsOpen(false);
688
+ // Only close if all ages are filled (when there are minors)
689
+ if (!hasMinorsWithoutAges() || areAllAgesFilled()) {
690
+ setIsOpen(false);
691
+ }
625
692
  }
626
693
  };
627
694
 
@@ -632,7 +699,7 @@ const PaxSelector: React.FC<PaxSelectorProps> = ({
632
699
  return () => {
633
700
  document.removeEventListener('mousedown', handleClickOutside);
634
701
  };
635
- }, [isOpen]);
702
+ }, [isOpen, internalData, rooms, multipleRooms]);
636
703
 
637
704
  // Scroll to input when dropdown opens (if scrollOnOpen is true)
638
705
  useEffect(() => {
@@ -704,8 +771,11 @@ const PaxSelector: React.FC<PaxSelectorProps> = ({
704
771
  };
705
772
 
706
773
  const handleDone = () => {
707
- setIsOpen(false);
708
- onDone?.(multipleRooms ? rooms : internalData);
774
+ // Only close if all ages are filled (when there are minors)
775
+ if (!hasMinorsWithoutAges() || areAllAgesFilled()) {
776
+ setIsOpen(false);
777
+ onDone?.(multipleRooms ? rooms : internalData);
778
+ }
709
779
  };
710
780
 
711
781
  const handleAddRoom = () => {
@@ -829,6 +899,7 @@ const PaxSelector: React.FC<PaxSelectorProps> = ({
829
899
  onRemove={() => handleRemoveRoom(room.roomId)}
830
900
  scrollToRef={index === rooms.length - 1 ? lastRoomRef : undefined}
831
901
  ageRange={ageRange}
902
+ checkEmpty={checkEmpty}
832
903
  />
833
904
  ))}
834
905
  </div>
@@ -935,6 +1006,7 @@ const PaxSelector: React.FC<PaxSelectorProps> = ({
935
1006
  }
936
1007
  ageRange={ageRange}
937
1008
  required
1009
+ error={checkEmpty}
938
1010
  />
939
1011
  );
940
1012
  })}
@@ -961,6 +1033,7 @@ const PaxSelector: React.FC<PaxSelectorProps> = ({
961
1033
  }
962
1034
  ageRange={ageRange}
963
1035
  required
1036
+ error={checkEmpty}
964
1037
  />
965
1038
  );
966
1039
  })}
@@ -987,6 +1060,7 @@ const PaxSelector: React.FC<PaxSelectorProps> = ({
987
1060
  }
988
1061
  ageRange={ageRange}
989
1062
  required
1063
+ error={checkEmpty}
990
1064
  />
991
1065
  );
992
1066
  })}
@@ -303,6 +303,7 @@ const RoundTrip: React.FC<RoundTripProps> = ({
303
303
  placeholder="2 pax"
304
304
  className={isPaxEmpty ? 'pax-selector--error' : ''}
305
305
  scrollOnOpen={scrollOnOpen}
306
+ checkEmpty={checkEmpty}
306
307
  />
307
308
  </div>
308
309
 
@@ -194,6 +194,42 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
194
194
  setTransferLines((prevLines) => prevLines.filter((line) => line.id !== id));
195
195
  };
196
196
 
197
+ // Helper function to check if all ages are filled for pax data
198
+ const areAllAgesFilled = (paxData: {
199
+ teens: number;
200
+ children: number;
201
+ infants: number;
202
+ teenAges?: number[];
203
+ childAges?: number[];
204
+ infantAges?: number[]
205
+ }): boolean => {
206
+ // Check teen ages
207
+ if (paxData.teens > 0) {
208
+ const teenAges = paxData.teenAges || [];
209
+ if (teenAges.length < paxData.teens) return false;
210
+ for (let i = 0; i < paxData.teens; i++) {
211
+ if (teenAges[i] === undefined || teenAges[i] === null) return false;
212
+ }
213
+ }
214
+ // Check child ages
215
+ if (paxData.children > 0) {
216
+ const childAges = paxData.childAges || [];
217
+ if (childAges.length < paxData.children) return false;
218
+ for (let i = 0; i < paxData.children; i++) {
219
+ if (childAges[i] === undefined || childAges[i] === null) return false;
220
+ }
221
+ }
222
+ // Check infant ages
223
+ if (paxData.infants > 0) {
224
+ const infantAges = paxData.infantAges || [];
225
+ if (infantAges.length < paxData.infants) return false;
226
+ for (let i = 0; i < paxData.infants; i++) {
227
+ if (infantAges[i] === undefined || infantAges[i] === null) return false;
228
+ }
229
+ }
230
+ return true;
231
+ };
232
+
197
233
  // Validate data
198
234
  const validateData = (): boolean => {
199
235
  if (mode === "roundtrip") {
@@ -212,6 +248,11 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
212
248
  setError("Please fill in all the transfer details.");
213
249
  return false;
214
250
  }
251
+ // Check if all ages are filled
252
+ if (!areAllAgesFilled(roundTripData.paxData)) {
253
+ setError("Please fill in all the transfer details.");
254
+ return false;
255
+ }
215
256
  } else {
216
257
  if (transferLines.length === 0) {
217
258
  setError("Please add at least one transfer.");
@@ -228,6 +269,11 @@ const SearchBarTransfer: React.FC<SearchBarTransferProps> = ({
228
269
  setError("Please fill in all the transfer details.");
229
270
  return false;
230
271
  }
272
+ // Check if all ages are filled for this line
273
+ if (!areAllAgesFilled(line.paxData)) {
274
+ setError("Please fill in all the transfer details.");
275
+ return false;
276
+ }
231
277
  }
232
278
  }
233
279
  setError(null);
@@ -320,6 +320,7 @@ const TransferLine: React.FC<TransferLineProps> = ({
320
320
  className={isPaxEmpty ? 'pax-selector--error' : ''}
321
321
  disabled={disabled}
322
322
  scrollOnOpen={scrollOnOpen}
323
+ checkEmpty={checkEmpty}
323
324
  />
324
325
  </div>
325
326
 
@@ -35,6 +35,26 @@
35
35
  @apply border-[var(--dropdown-color-border-hover,#0f7173)];
36
36
  }
37
37
 
38
+ .age-selector__input--error {
39
+ @apply border-[var(--color-red-600,#dc2626)] bg-[var(--color-coral-red-50,#fff8f8)];
40
+ }
41
+
42
+ .age-selector__input--error:hover {
43
+ @apply border-[var(--color-red-600,#dc2626)];
44
+ }
45
+
46
+ .age-selector__input--error .age-selector__input-field {
47
+ @apply text-[var(--color-coral-red-600,#f83b3b)];
48
+ }
49
+
50
+ .age-selector__input--error .age-selector__input-field::placeholder {
51
+ @apply text-[var(--color-coral-red-600,#f83b3b)];
52
+ }
53
+
54
+ .age-selector__input--error .age-selector__icon {
55
+ @apply text-[var(--color-coral-red-600,#f83b3b)];
56
+ }
57
+
38
58
  .age-selector__input--default .age-selector__input-field::placeholder {
39
59
  @apply text-[var(--dropdown-selector-color-filled-foreground-default,#737373)];
40
60
  }