@qite/tide-booking-component 1.4.107 → 1.4.109

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.
@@ -434,107 +434,109 @@ const QSMContainer: React.FC = () => {
434
434
  {translations.QSM.CRUISES}
435
435
  </button> */}
436
436
  </div>
437
- <div className="qsm__filter">
438
- {(qsmType === PortalQsmType.Accommodation || qsmType === PortalQsmType.AccommodationAndFlight || qsmType === PortalQsmType.GroupTour) && (
439
- <div className="radiobutton-group qsm__filter__inputgroup">
440
- <div className="radiobutton">
441
- <label className="radiobutton__label">
442
- <input
443
- type="radio"
444
- name="numberOfAccommodations"
445
- // onChange={handleMainBookerChange}
446
- // onBlur={formik.handleBlur}
447
- value=""
448
- checked={true}
449
- readOnly
450
- className="radiobutton__input"
451
- />
452
- <span>{translations.QSM.ONE_ACCOMMODATION}</span>
453
- </label>
454
- </div>
455
- <div className="radiobutton">
456
- <label className="radiobutton__label">
457
- <input
458
- type="radio"
459
- name="numberOfAccommodations"
460
- // onChange={handleMainBookerChange}
461
- // onBlur={formik.handleBlur}
462
- value=""
463
- className="radiobutton__input"
464
- disabled={true}
465
- />
466
- <span>{translations.QSM.MULTIPLE_ACCOMMODATIONS}</span>
467
- </label>
468
- </div>
469
- </div>
470
- )}
471
- {qsmType === PortalQsmType.Flight && (
472
- <div className="radiobutton-group qsm__filter__inputgroup">
473
- {allowOneWay && (
437
+ {!isMobile && (
438
+ <div className="qsm__filter">
439
+ {(qsmType === PortalQsmType.Accommodation || qsmType === PortalQsmType.AccommodationAndFlight || qsmType === PortalQsmType.GroupTour) && (
440
+ <div className="radiobutton-group qsm__filter__inputgroup">
474
441
  <div className="radiobutton">
475
442
  <label className="radiobutton__label">
476
443
  <input
477
444
  type="radio"
478
- name="tripType"
479
- value="oneway"
480
- checked={tripType === 'oneway'}
481
- onChange={() => handleTripTypeChange('oneway')}
445
+ name="numberOfAccommodations"
446
+ // onChange={handleMainBookerChange}
447
+ // onBlur={formik.handleBlur}
448
+ value=""
449
+ checked={true}
450
+ readOnly
482
451
  className="radiobutton__input"
483
452
  />
484
- <span>{translations.QSM.ONEWAY}</span>
453
+ <span>{translations.QSM.ONE_ACCOMMODATION}</span>
485
454
  </label>
486
455
  </div>
487
- )}
488
-
489
- {allowRoundtrip && (
490
456
  <div className="radiobutton">
491
457
  <label className="radiobutton__label">
492
458
  <input
493
459
  type="radio"
494
- name="tripType"
495
- value="roundtrip"
496
- checked={tripType === 'roundtrip'}
497
- onChange={() => handleTripTypeChange('roundtrip')}
460
+ name="numberOfAccommodations"
461
+ // onChange={handleMainBookerChange}
462
+ // onBlur={formik.handleBlur}
463
+ value=""
498
464
  className="radiobutton__input"
465
+ disabled={true}
499
466
  />
500
- <span>{translations.QSM.ROUNDTRIP}</span>
467
+ <span>{translations.QSM.MULTIPLE_ACCOMMODATIONS}</span>
501
468
  </label>
502
469
  </div>
503
- )}
504
-
505
- {allowOpenJaw && (
506
- <div className="radiobutton">
507
- <label className="radiobutton__label">
508
- <input
509
- type="radio"
510
- name="tripType"
511
- value="openjaw"
512
- checked={tripType === 'openjaw'}
513
- onChange={() => handleTripTypeChange('openjaw')}
514
- className="radiobutton__input"
515
- />
516
- <span>{translations.QSM.OPENJAW}</span>
517
- </label>
518
- </div>
519
- )}
470
+ </div>
471
+ )}
472
+ {qsmType === PortalQsmType.Flight && (
473
+ <div className="radiobutton-group qsm__filter__inputgroup">
474
+ {allowOneWay && (
475
+ <div className="radiobutton">
476
+ <label className="radiobutton__label">
477
+ <input
478
+ type="radio"
479
+ name="tripType"
480
+ value="oneway"
481
+ checked={tripType === 'oneway'}
482
+ onChange={() => handleTripTypeChange('oneway')}
483
+ className="radiobutton__input"
484
+ />
485
+ <span>{translations.QSM.ONEWAY}</span>
486
+ </label>
487
+ </div>
488
+ )}
489
+
490
+ {allowRoundtrip && (
491
+ <div className="radiobutton">
492
+ <label className="radiobutton__label">
493
+ <input
494
+ type="radio"
495
+ name="tripType"
496
+ value="roundtrip"
497
+ checked={tripType === 'roundtrip'}
498
+ onChange={() => handleTripTypeChange('roundtrip')}
499
+ className="radiobutton__input"
500
+ />
501
+ <span>{translations.QSM.ROUNDTRIP}</span>
502
+ </label>
503
+ </div>
504
+ )}
505
+
506
+ {allowOpenJaw && (
507
+ <div className="radiobutton">
508
+ <label className="radiobutton__label">
509
+ <input
510
+ type="radio"
511
+ name="tripType"
512
+ value="openjaw"
513
+ checked={tripType === 'openjaw'}
514
+ onChange={() => handleTripTypeChange('openjaw')}
515
+ className="radiobutton__input"
516
+ />
517
+ <span>{translations.QSM.OPENJAW}</span>
518
+ </label>
519
+ </div>
520
+ )}
521
+ </div>
522
+ )}
523
+ <div className="qsm__filter__classgroup">
524
+ {qsmType !== PortalQsmType.Accommodation &&
525
+ qsmType !== PortalQsmType.Car &&
526
+ qsmType !== PortalQsmType.Ticket &&
527
+ qsmType !== PortalQsmType.Cruise &&
528
+ qsmType !== PortalQsmType.Transfer &&
529
+ qsmType !== PortalQsmType.GroupTour &&
530
+ askTravelClass && <TravelClassPicker />}
531
+ {qsmType !== PortalQsmType.Multidestination &&
532
+ qsmType !== PortalQsmType.Car &&
533
+ qsmType !== PortalQsmType.Flight &&
534
+ qsmType !== PortalQsmType.Transfer &&
535
+ askTravelType && <TravelTypePicker />}
536
+ {askNationality && <TravelNationalityPicker />}
520
537
  </div>
521
- )}
522
- <div className="qsm__filter__classgroup">
523
- {qsmType !== PortalQsmType.Accommodation &&
524
- qsmType !== PortalQsmType.Car &&
525
- qsmType !== PortalQsmType.Ticket &&
526
- qsmType !== PortalQsmType.Cruise &&
527
- qsmType !== PortalQsmType.Transfer &&
528
- qsmType !== PortalQsmType.GroupTour &&
529
- askTravelClass && <TravelClassPicker />}
530
- {qsmType !== PortalQsmType.Multidestination &&
531
- qsmType !== PortalQsmType.Car &&
532
- qsmType !== PortalQsmType.Flight &&
533
- qsmType !== PortalQsmType.Transfer &&
534
- askTravelType && <TravelTypePicker />}
535
- {askNationality && <TravelNationalityPicker />}
536
538
  </div>
537
- </div>
539
+ )}
538
540
  <div className="qsm__input-group">
539
541
  {/* TODO, determine which fields to show for what type of QSM */}
540
542
  {(qsmType == PortalQsmType.Flight || qsmType == PortalQsmType.AccommodationAndFlight) && originDestinationField && (
@@ -551,6 +553,110 @@ const QSMContainer: React.FC = () => {
551
553
 
552
554
  {askTravelers && <TravelInputGroup />}
553
555
 
556
+ {isMobile && (
557
+ <div className="qsm__filter">
558
+ {(qsmType === PortalQsmType.Accommodation || qsmType === PortalQsmType.AccommodationAndFlight || qsmType === PortalQsmType.GroupTour) && (
559
+ <div className="radiobutton-group qsm__filter__inputgroup">
560
+ <div className="radiobutton">
561
+ <label className="radiobutton__label">
562
+ <input
563
+ type="radio"
564
+ name="numberOfAccommodations"
565
+ // onChange={handleMainBookerChange}
566
+ // onBlur={formik.handleBlur}
567
+ value=""
568
+ checked={true}
569
+ readOnly
570
+ className="radiobutton__input"
571
+ />
572
+ <span>{translations.QSM.ONE_ACCOMMODATION}</span>
573
+ </label>
574
+ </div>
575
+ <div className="radiobutton">
576
+ <label className="radiobutton__label">
577
+ <input
578
+ type="radio"
579
+ name="numberOfAccommodations"
580
+ // onChange={handleMainBookerChange}
581
+ // onBlur={formik.handleBlur}
582
+ value=""
583
+ className="radiobutton__input"
584
+ disabled={true}
585
+ />
586
+ <span>{translations.QSM.MULTIPLE_ACCOMMODATIONS}</span>
587
+ </label>
588
+ </div>
589
+ </div>
590
+ )}
591
+ {qsmType === PortalQsmType.Flight && (
592
+ <div className="radiobutton-group qsm__filter__inputgroup">
593
+ {allowOneWay && (
594
+ <div className="radiobutton">
595
+ <label className="radiobutton__label">
596
+ <input
597
+ type="radio"
598
+ name="tripType"
599
+ value="oneway"
600
+ checked={tripType === 'oneway'}
601
+ onChange={() => handleTripTypeChange('oneway')}
602
+ className="radiobutton__input"
603
+ />
604
+ <span>{translations.QSM.ONEWAY}</span>
605
+ </label>
606
+ </div>
607
+ )}
608
+
609
+ {allowRoundtrip && (
610
+ <div className="radiobutton">
611
+ <label className="radiobutton__label">
612
+ <input
613
+ type="radio"
614
+ name="tripType"
615
+ value="roundtrip"
616
+ checked={tripType === 'roundtrip'}
617
+ onChange={() => handleTripTypeChange('roundtrip')}
618
+ className="radiobutton__input"
619
+ />
620
+ <span>{translations.QSM.ROUNDTRIP}</span>
621
+ </label>
622
+ </div>
623
+ )}
624
+
625
+ {allowOpenJaw && (
626
+ <div className="radiobutton">
627
+ <label className="radiobutton__label">
628
+ <input
629
+ type="radio"
630
+ name="tripType"
631
+ value="openjaw"
632
+ checked={tripType === 'openjaw'}
633
+ onChange={() => handleTripTypeChange('openjaw')}
634
+ className="radiobutton__input"
635
+ />
636
+ <span>{translations.QSM.OPENJAW}</span>
637
+ </label>
638
+ </div>
639
+ )}
640
+ </div>
641
+ )}
642
+ <div className="qsm__filter__classgroup">
643
+ {qsmType !== PortalQsmType.Accommodation &&
644
+ qsmType !== PortalQsmType.Car &&
645
+ qsmType !== PortalQsmType.Ticket &&
646
+ qsmType !== PortalQsmType.Cruise &&
647
+ qsmType !== PortalQsmType.Transfer &&
648
+ qsmType !== PortalQsmType.GroupTour &&
649
+ askTravelClass && <TravelClassPicker />}
650
+ {qsmType !== PortalQsmType.Multidestination &&
651
+ qsmType !== PortalQsmType.Car &&
652
+ qsmType !== PortalQsmType.Flight &&
653
+ qsmType !== PortalQsmType.Transfer &&
654
+ askTravelType && <TravelTypePicker />}
655
+ {askNationality && <TravelNationalityPicker />}
656
+ </div>
657
+ </div>
658
+ )}
659
+
554
660
  <button type="button" className="cta" onClick={handleSubmit}>
555
661
  {submitIcon && submitIcon.toString().length > 0 && <span>{submitIcon}</span>}
556
662
  <span>{translations.QSM.CONFIRM}</span>
@@ -14,7 +14,6 @@ interface ItemPickerProps {
14
14
  const ItemPicker: React.FC<ItemPickerProps> = ({ items, selection, label, placeholder, classModifier, valueFormatter, onPick }) => {
15
15
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
16
16
  const dropdownRef = useRef<HTMLDivElement | null>(null);
17
- const toggleButtonRef = useRef<HTMLButtonElement | null>(null);
18
17
 
19
18
  const handlePick = (picked: string, id?: string) => {
20
19
  setIsDropdownOpen(false);
@@ -37,11 +36,8 @@ const ItemPicker: React.FC<ItemPickerProps> = ({ items, selection, label, placeh
37
36
  return (
38
37
  <div className={'dropdown__input ' + classModifier}>
39
38
  <span className="dropdown__label">{label}</span>
40
- <div className="dropdown">
41
- <button
42
- className={`dropdown-toggle ${isDropdownOpen ? 'dropdown-toggle--open' : ''}`}
43
- onClick={() => setIsDropdownOpen((prev) => !prev)}
44
- ref={toggleButtonRef}>
39
+ <div className="dropdown" ref={dropdownRef}>
40
+ <button className={`dropdown-toggle ${isDropdownOpen ? 'dropdown-toggle--open' : ''}`} onClick={() => setIsDropdownOpen((prev) => !prev)}>
45
41
  <span>{selection || placeholder}</span>
46
42
  <span className="arrow">&#9662;</span>
47
43
  </button>
@@ -35,6 +35,8 @@ const MobileFilterModal: React.FC = () => {
35
35
  destination
36
36
  } = useSelector((state: QSMRootState) => state.qsm);
37
37
 
38
+ const searchInputRef = useRef<HTMLInputElement>(null);
39
+
38
40
  const [inputValue, setInputValue] = useState('');
39
41
 
40
42
  /* ---------------------------------------------------------------- */
@@ -48,6 +50,18 @@ const MobileFilterModal: React.FC = () => {
48
50
  }
49
51
  }, [activeSearchFieldProps]);
50
52
 
53
+ useEffect(() => {
54
+ if (mobileFilterType !== 'search') return;
55
+
56
+ requestAnimationFrame(() => {
57
+ const input = searchInputRef.current;
58
+ if (!input) return;
59
+
60
+ input.focus();
61
+ input.setSelectionRange(input.value.length, input.value.length);
62
+ });
63
+ }, [mobileFilterType, activeSearchFieldProps?.fieldKey]);
64
+
51
65
  /* ---------------------------------------------------------------- */
52
66
  /* Helpers */
53
67
  /* ---------------------------------------------------------------- */
@@ -229,12 +243,12 @@ const MobileFilterModal: React.FC = () => {
229
243
  <div className="qsm__double-input qsm__double-input--search-modal">
230
244
  <label className="qsm__input-wrapper">
231
245
  <input
246
+ ref={searchInputRef}
232
247
  type="text"
233
248
  id="search"
234
249
  value={inputValue}
235
250
  onClick={(e) => e.stopPropagation()}
236
251
  onChange={(e) => handleInputChange(e.target.value)}
237
- // onChange={(e) => handleLocationChange(e.target.value)}
238
252
  className="qsm__input qsm__input--modal qsm__from-to u-ps-2"
239
253
  placeholder={activeSearchFieldProps.placeholder}
240
254
  />
@@ -1079,6 +1079,20 @@ const Icon: React.FC<IconProps> = ({ name, className, title, width, height, fill
1079
1079
  </svg>
1080
1080
  );
1081
1081
 
1082
+ case 'ui-triangle-error':
1083
+ return (
1084
+ <svg
1085
+ className={['icon', `icon--${name}`, className].filter((className) => !isEmpty(className)).join(' ')}
1086
+ width={width}
1087
+ height={height}
1088
+ viewBox="0 0 512 512"
1089
+ fill={fill ?? 'currentColor'}>
1090
+ <HTMLComment text="!Font Awesome Free v7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc." />
1091
+ {title && <title>{title}</title>}
1092
+ <path d="M256 0c14.7 0 28.2 8.1 35.2 21l216 400c6.7 12.4 6.4 27.4-.8 39.5S486.1 480 472 480L40 480c-14.1 0-27.2-7.4-34.4-19.5s-7.5-27.1-.8-39.5l216-400c7-12.9 20.5-21 35.2-21zm0 352a32 32 0 1 0 0 64 32 32 0 1 0 0-64zm0-192c-18.2 0-32.7 15.5-31.4 33.7l7.4 104c.9 12.5 11.4 22.3 23.9 22.3 12.6 0 23-9.7 23.9-22.3l7.4-104c1.3-18.2-13.1-33.7-31.4-33.7z" />
1093
+ </svg>
1094
+ );
1095
+
1082
1096
  default:
1083
1097
  return null;
1084
1098
  }
@@ -0,0 +1,50 @@
1
+ @use '../components/mixins' as mixins;
2
+
3
+ .error {
4
+ padding: 60px 0px;
5
+ background: var(--tide-booking-bg);
6
+
7
+ &__container {
8
+ @include mixins.container-wide();
9
+ display: flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ height: 100%;
13
+ }
14
+
15
+ &__card {
16
+ display: flex;
17
+ flex-direction: column;
18
+ align-items: center;
19
+ }
20
+
21
+ &__icon {
22
+ border-radius: 20%;
23
+ display: flex;
24
+ font-size: 24px;
25
+ font-weight: 600;
26
+ align-items: center;
27
+ justify-content: center;
28
+ margin-bottom: 20px;
29
+ color: var(--tide-booking-invalid);
30
+
31
+ @include media-lg {
32
+ font-size: 50px;
33
+ }
34
+
35
+ svg {
36
+ width: 40px;
37
+ height: 40px;
38
+
39
+ @include media-lg {
40
+ width: 80px;
41
+ height: 80px;
42
+ }
43
+ }
44
+ }
45
+
46
+ &__link {
47
+ max-width: 150px;
48
+ margin-top: 1rem;
49
+ }
50
+ }
@@ -1,3 +1,4 @@
1
+ @use '../components/mixins' as mixins;
1
2
  .faq {
2
3
  padding: 30px 0;
3
4
 
@@ -16,14 +16,8 @@
16
16
 
17
17
  &__top {
18
18
  display: flex;
19
- flex-direction: column;
20
- gap: 20px;
21
-
22
- @include mixins.media-xxs {
23
- flex-direction: row;
24
- justify-content: space-between;
25
- align-items: center;
26
- }
19
+ justify-content: space-between;
20
+ align-items: center;
27
21
  }
28
22
 
29
23
  &__logo {
@@ -134,12 +134,12 @@
134
134
  &--close {
135
135
  position: fixed;
136
136
  z-index: 100020;
137
- top: 30px;
138
- right: 30px;
137
+ top: 10px;
138
+ right: 15px;
139
139
  display: none;
140
140
  background: none;
141
141
  border: none;
142
- color: white;
142
+ color: var(--tide-booking-search-filter-price-amount-color);
143
143
  cursor: pointer;
144
144
  }
145
145
 
@@ -180,8 +180,8 @@
180
180
  .search__filters {
181
181
  position: relative;
182
182
  display: block;
183
- max-width: 360px;
184
- width: 360px;
183
+ max-width: 100%;
184
+ width: 100%;
185
185
  height: 100%;
186
186
  margin: 0;
187
187
  border: none;
@@ -26,6 +26,7 @@ body {
26
26
  @import './components/login';
27
27
  @import './components/accordion';
28
28
  @import './components/footer';
29
+ @import './components/error';
29
30
 
30
31
  @import './qsm/qsm';
31
32
 
@@ -521,6 +521,9 @@
521
521
  // border-bottom: 4px solid variables.$secondary-color;
522
522
  display: flex;
523
523
  flex-direction: column;
524
+ max-height: 300px;
525
+ overflow: auto;
526
+ @extend %scrollbar-thin;
524
527
 
525
528
  @include mixins.media-md {
526
529
  width: 100%;