kupos-ui-components-lib 10.4.58 → 10.4.60

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.
Files changed (30) hide show
  1. package/dist/components/FilterBar/FilterBarDesktop.d.ts +1 -1
  2. package/dist/components/FilterBar/FilterBarDesktop.js +2 -2
  3. package/dist/components/FilterBar/ServiceFilter.js +8 -1
  4. package/dist/components/FilterBar/ServiceFilterProps.d.ts +1 -0
  5. package/dist/components/FilterBar/tyoes.d.ts +6 -0
  6. package/dist/components/FilterSortBar/FilterSortBarMobile.d.ts +1 -1
  7. package/dist/components/FilterSortBar/FilterSortBarMobile.js +2 -2
  8. package/dist/components/FilterSortBar/FiltersMasterSheet.d.ts +2 -1
  9. package/dist/components/FilterSortBar/FiltersMasterSheet.js +2 -2
  10. package/dist/components/FilterSortBar/OptionCheckboxList.d.ts +3 -1
  11. package/dist/components/FilterSortBar/OptionCheckboxList.js +6 -2
  12. package/dist/components/FilterSortBar/types.d.ts +7 -0
  13. package/dist/ui/SeatSection/PeruSeatSection.js +1 -1
  14. package/dist/ui/SeatSection/SeatSection.js +1 -1
  15. package/dist/ui/mobileweb/SeatSectionMobile.js +1 -1
  16. package/dist/utils/CommonService.d.ts +2 -0
  17. package/dist/utils/CommonService.js +9 -0
  18. package/package.json +1 -1
  19. package/src/components/FilterBar/FilterBarDesktop.tsx +2 -0
  20. package/src/components/FilterBar/ServiceFilter.tsx +18 -1
  21. package/src/components/FilterBar/ServiceFilterProps.ts +1 -0
  22. package/src/components/FilterBar/tyoes.ts +6 -0
  23. package/src/components/FilterSortBar/FilterSortBarMobile.tsx +2 -0
  24. package/src/components/FilterSortBar/FiltersMasterSheet.tsx +3 -0
  25. package/src/components/FilterSortBar/OptionCheckboxList.tsx +42 -20
  26. package/src/components/FilterSortBar/types.ts +7 -0
  27. package/src/ui/SeatSection/PeruSeatSection.tsx +1 -1
  28. package/src/ui/SeatSection/SeatSection.tsx +1 -1
  29. package/src/ui/mobileweb/SeatSectionMobile.tsx +1 -1
  30. package/src/utils/CommonService.ts +10 -0
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
2
  import { FilterBarProps } from "./tyoes";
3
- declare const FilterBarDesktop: ({ t, serviceList, setServiceList, allSchedules, showReturnTripModal, filterArrData, isBlankTicket, operatorId, filtersArray, setFiltersArray, metaData, busTerminals, setTypes, colors, icons, translation, isTrain, isPeru, hideOperator, setCoachLoading, setCoachKey, isAllinBus, priority_stage_ids, }: FilterBarProps) => React.JSX.Element;
3
+ declare const FilterBarDesktop: ({ t, serviceList, setServiceList, allSchedules, showReturnTripModal, filterArrData, isBlankTicket, operatorId, filtersArray, setFiltersArray, metaData, busTerminals, setTypes, colors, icons, translation, isTrain, isPeru, hideOperator, isShowSeatDegree, setCoachLoading, setCoachKey, isAllinBus, priority_stage_ids, }: FilterBarProps) => React.JSX.Element;
4
4
  export default FilterBarDesktop;
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import commonService from "../../utils/CommonService";
3
3
  import ServiceFilter from "./ServiceFilter";
4
4
  let cachedStageOptions = null;
5
- const FilterBarDesktop = ({ t, serviceList, setServiceList, allSchedules, showReturnTripModal, filterArrData, isBlankTicket, operatorId, filtersArray, setFiltersArray, metaData, busTerminals, setTypes, colors, icons, translation, isTrain, isPeru, hideOperator, setCoachLoading, setCoachKey, isAllinBus, priority_stage_ids, }) => {
5
+ const FilterBarDesktop = ({ t, serviceList, setServiceList, allSchedules, showReturnTripModal, filterArrData, isBlankTicket, operatorId, filtersArray, setFiltersArray, metaData, busTerminals, setTypes, colors, icons, translation, isTrain, isPeru, hideOperator, isShowSeatDegree, setCoachLoading, setCoachKey, isAllinBus, priority_stage_ids, }) => {
6
6
  // Same rule as showTopLabel in ServiceItemDesktop: a visible bus terminal
7
7
  // label, restricted to priority_stage_ids when that list is non-empty.
8
8
  const getTopLabel = (labelId) => {
@@ -267,6 +267,6 @@ const FilterBarDesktop = ({ t, serviceList, setServiceList, allSchedules, showRe
267
267
  return (React.createElement(ServiceFilter, { filtersArray: enrichedFilters, onFilterSelected: onFilterSelected, onFilterClear: (val) => {
268
268
  setFiltersArray(val);
269
269
  filterBy(val);
270
- }, showReturnTripModal: showReturnTripModal, initialOpenFilters: isBlankTicket ? ["operator"] : [], colors: colors, icons: icons, translation: translation, isTrain: isTrain, hideOperator: hideOperator, t: t }));
270
+ }, showReturnTripModal: showReturnTripModal, initialOpenFilters: isBlankTicket ? ["operator"] : [], colors: colors, icons: icons, translation: translation, isTrain: isTrain, hideOperator: hideOperator, isShowSeatDegree: isShowSeatDegree, t: t }));
271
271
  };
272
272
  export default FilterBarDesktop;
@@ -225,6 +225,9 @@ class ServiceFilter extends React.Component {
225
225
  ? val.spText
226
226
  : val.label;
227
227
  }
228
+ const seatDegreeIcon = option.type === "seat_types" && this.props.isShowSeatDegree
229
+ ? commonService.getSeatDegreeIcon(label, this.props.icons)
230
+ : null;
228
231
  return (React.createElement("div", { key: i, onClick: () => onClick(val.originalOptionIndex !== undefined
229
232
  ? val.originalOptionIndex
230
233
  : i), style: {
@@ -240,11 +243,15 @@ class ServiceFilter extends React.Component {
240
243
  ? `text-[${this.props.colors.selectedTextColor}]`
241
244
  : ""} ${val.active ? "bold-text" : ""}` },
242
245
  val.icon && this.props.icons && (React.createElement("img", { src: iconKey, alt: val.icon, className: "w-[20px] h-[20px] mr-[10px] " })),
246
+ seatDegreeIcon && (React.createElement("img", { src: seatDegreeIcon, alt: "seattype", className: "w-[20px] h-[20px] mr-[10px]" })),
243
247
  React.createElement("span", { style: {
244
248
  textTransform: option.type !== "time" ? "capitalize" : "none",
245
249
  } }, option.type === "time"
246
250
  ? commonService.formatTimeLabel(label)
247
- : commonService.formatDefaultLabel(label))));
251
+ : option.type === "seat_types" &&
252
+ this.props.isShowSeatDegree
253
+ ? commonService.addSeatDegree(commonService.formatDefaultLabel(label))
254
+ : commonService.formatDefaultLabel(label))));
248
255
  }))));
249
256
  }
250
257
  render() {
@@ -9,5 +9,6 @@ export interface ServiceFilterProps {
9
9
  translation: any;
10
10
  isTrain?: boolean;
11
11
  hideOperator?: boolean;
12
+ isShowSeatDegree?: boolean;
12
13
  t?: (key: string) => string;
13
14
  }
@@ -14,6 +14,7 @@ export interface FilterBarProps {
14
14
  busTerminals?: any;
15
15
  setTypes?: any;
16
16
  hideOperator?: boolean;
17
+ isShowSeatDegree?: boolean;
17
18
  colors: {
18
19
  selectedColor?: string;
19
20
  selectedTextColor?: string;
@@ -27,6 +28,11 @@ export interface FilterBarProps {
27
28
  iconSunsetOrange?: string;
28
29
  iconNight?: string;
29
30
  iconNightOrange?: string;
31
+ seat120?: string;
32
+ seat140?: string;
33
+ seat160?: string;
34
+ seat180?: string;
35
+ fallBackSeatIcon?: string;
30
36
  };
31
37
  translation?: {
32
38
  [key: string]: string;
@@ -6,5 +6,5 @@ import { FilterSortBarMobileProps } from "./types";
6
6
  * consumer's list — sees a change until Aplicar commits the draft via
7
7
  * onApply. Closing the sheet without applying discards it.
8
8
  */
9
- declare const FilterSortBarMobile: ({ categories, onApply, onClearFilter, sortOptions, onSortChange, currencySign, colors, icons, }: FilterSortBarMobileProps) => React.JSX.Element;
9
+ declare const FilterSortBarMobile: ({ categories, onApply, onClearFilter, sortOptions, onSortChange, currencySign, colors, icons, isShowSeatDegree, }: FilterSortBarMobileProps) => React.JSX.Element;
10
10
  export default FilterSortBarMobile;
@@ -45,7 +45,7 @@ const isCategoryActive = (category) => getCategoryActiveCount(category) > 0;
45
45
  * consumer's list — sees a change until Aplicar commits the draft via
46
46
  * onApply. Closing the sheet without applying discards it.
47
47
  */
48
- const FilterSortBarMobile = ({ categories, onApply, onClearFilter, sortOptions, onSortChange, currencySign = "$", colors, icons, }) => {
48
+ const FilterSortBarMobile = ({ categories, onApply, onClearFilter, sortOptions, onSortChange, currencySign = "$", colors, icons, isShowSeatDegree = false, }) => {
49
49
  const [openSheet, setOpenSheet] = useState(null);
50
50
  const [isScrolled, setIsScrolled] = useState(false);
51
51
  const [draftCategories, setDraftCategories] = useState(categories);
@@ -164,6 +164,6 @@ const FilterSortBarMobile = ({ categories, onApply, onClearFilter, sortOptions,
164
164
  var _a;
165
165
  return (React.createElement(FilterChip, { key: `${category.key}:${option.id}`, label: option.label, active: option.selected, isOpen: false, onClick: () => handleInlineOptionToggle(category.key, option.id), iconSrc: option.icon ? (_a = resolvedIcons.timeOfDayIcons) === null || _a === void 0 ? void 0 : _a[option.icon] : undefined, colors: resolvedColors, icons: resolvedIcons }));
166
166
  })))),
167
- React.createElement(FiltersMasterSheet, { isOpen: isMasterOpen, initialCategoryKey: initialCategoryKey, categories: draftCategories, onClose: closeSheet, onToggleOption: handleDraftToggleOption, onPriceRangeChange: handleDraftPriceRangeChange, onClearAll: handleClearAll, onApplyAll: handleApplyAll, currencySign: currencySign, colors: resolvedColors, icons: resolvedIcons })));
167
+ React.createElement(FiltersMasterSheet, { isOpen: isMasterOpen, initialCategoryKey: initialCategoryKey, categories: draftCategories, onClose: closeSheet, onToggleOption: handleDraftToggleOption, onPriceRangeChange: handleDraftPriceRangeChange, onClearAll: handleClearAll, onApplyAll: handleApplyAll, currencySign: currencySign, colors: resolvedColors, icons: resolvedIcons, isShowSeatDegree: isShowSeatDegree })));
168
168
  };
169
169
  export default FilterSortBarMobile;
@@ -13,6 +13,7 @@ interface FiltersMasterSheetProps {
13
13
  currencySign: string;
14
14
  colors: Required<FilterSortBarColors>;
15
15
  icons?: FilterSortBarIcons;
16
+ isShowSeatDegree?: boolean;
16
17
  }
17
- declare const FiltersMasterSheet: ({ isOpen, initialCategoryKey, categories, onClose, onToggleOption, onPriceRangeChange, onClearAll, onApplyAll, currencySign, colors, icons, }: FiltersMasterSheetProps) => React.JSX.Element;
18
+ declare const FiltersMasterSheet: ({ isOpen, initialCategoryKey, categories, onClose, onToggleOption, onPriceRangeChange, onClearAll, onApplyAll, currencySign, colors, icons, isShowSeatDegree, }: FiltersMasterSheetProps) => React.JSX.Element;
18
19
  export default FiltersMasterSheet;
@@ -4,7 +4,7 @@ import { CloseIcon } from "./icons";
4
4
  import OptionCheckboxList from "./OptionCheckboxList";
5
5
  import PriceRangeSlider from "./PriceRangeSlider";
6
6
  import SheetFooter from "./SheetFooter";
7
- const FiltersMasterSheet = ({ isOpen, initialCategoryKey, categories, onClose, onToggleOption, onPriceRangeChange, onClearAll, onApplyAll, currencySign, colors, icons, }) => {
7
+ const FiltersMasterSheet = ({ isOpen, initialCategoryKey, categories, onClose, onToggleOption, onPriceRangeChange, onClearAll, onApplyAll, currencySign, colors, icons, isShowSeatDegree, }) => {
8
8
  var _a, _b;
9
9
  const [activeKey, setActiveKey] = useState((_b = initialCategoryKey !== null && initialCategoryKey !== void 0 ? initialCategoryKey : (_a = categories[0]) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : "time");
10
10
  // Reset the pre-selected tab every time the sheet is (re)opened, whether
@@ -35,7 +35,7 @@ const FiltersMasterSheet = ({ isOpen, initialCategoryKey, categories, onClose, o
35
35
  })),
36
36
  React.createElement("div", { className: "flex-1 overflow-y-auto pl-[16px] pr-[20px] relative z-[1] bg-white" },
37
37
  activeCategory && (React.createElement("div", { className: "text-[11px] font-bold text-[#9B9B9B] uppercase tracking-[0.4px] mb-[5px] mt-[15px]" }, activeCategory.sectionLabel || activeCategory.label)),
38
- (activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.kind) === "price" && activeCategory.priceRange ? (React.createElement(PriceRangeSlider, { value: activeCategory.priceRange, onChange: (min, max) => onPriceRangeChange(activeCategory.key, min, max), currencySign: currencySign, primaryColor: colors.primaryColor, minLabel: "M\u00EDnimo", maxLabel: "M\u00E1ximo" })) : (React.createElement(OptionCheckboxList, { options: (activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.options) || [], kind: (activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.kind) || "checkbox", onToggle: (optionId) => onToggleOption(activeCategory.key, optionId), primaryColor: colors.primaryColor, starColor: colors.buttonColor || colors.primaryColor, icons: icons })))),
38
+ (activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.kind) === "price" && activeCategory.priceRange ? (React.createElement(PriceRangeSlider, { value: activeCategory.priceRange, onChange: (min, max) => onPriceRangeChange(activeCategory.key, min, max), currencySign: currencySign, primaryColor: colors.primaryColor, minLabel: "M\u00EDnimo", maxLabel: "M\u00E1ximo" })) : (React.createElement(OptionCheckboxList, { options: (activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.options) || [], kind: (activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.kind) || "checkbox", onToggle: (optionId) => onToggleOption(activeCategory.key, optionId), primaryColor: colors.primaryColor, starColor: colors.buttonColor || colors.primaryColor, icons: icons, showSeatDegree: !!isShowSeatDegree && (activeCategory === null || activeCategory === void 0 ? void 0 : activeCategory.key) === "seatType" })))),
39
39
  React.createElement("div", { className: "px-[20px]" },
40
40
  React.createElement(SheetFooter, { onClear: onClearAll, onApply: onApplyAll, colors: colors })))));
41
41
  };
@@ -8,6 +8,8 @@ interface OptionCheckboxListProps {
8
8
  /** Filled-star color for rating options — the app's button/CTA color, not the chip accent. */
9
9
  starColor: string;
10
10
  icons?: FilterSortBarIcons;
11
+ /** For options whose label contains 120/140/160/180: add "°" and show icons.seat120/140/160/180. */
12
+ showSeatDegree?: boolean;
11
13
  }
12
- declare const OptionCheckboxList: ({ options, kind, onToggle, primaryColor, starColor, icons }: OptionCheckboxListProps) => React.JSX.Element;
14
+ declare const OptionCheckboxList: ({ options, kind, onToggle, primaryColor, starColor, icons, showSeatDegree, }: OptionCheckboxListProps) => React.JSX.Element;
13
15
  export default OptionCheckboxList;
@@ -4,16 +4,20 @@ import CommonService from "../../utils/CommonService";
4
4
  const RatingLabel = ({ stars, label, starColor, icons, }) => (React.createElement("div", { className: "flex items-center gap-[1px] whitespace-nowrap" },
5
5
  Array.from({ length: 5 }, (_, i) => (React.createElement(StarIcon, { key: i, size: 10, filled: stars != null && i < stars, color: starColor, filledSrc: icons === null || icons === void 0 ? void 0 : icons.starFilledIcon, emptySrc: icons === null || icons === void 0 ? void 0 : icons.starEmptyIcon }))),
6
6
  React.createElement("span", { className: "ml-[6px]" }, label)));
7
- const OptionCheckboxList = ({ options, kind, onToggle, primaryColor, starColor, icons }) => {
7
+ const OptionCheckboxList = ({ options, kind, onToggle, primaryColor, starColor, icons, showSeatDegree, }) => {
8
8
  if (!(options === null || options === void 0 ? void 0 : options.length)) {
9
9
  return (React.createElement("div", { className: "py-[24px] text-center text-[#9B9B9B] text-[13px]" }, "No hay opciones disponibles"));
10
10
  }
11
11
  return (React.createElement("div", null, options.map((option) => {
12
12
  var _a;
13
+ const seatIcon = showSeatDegree ? CommonService.getSeatDegreeIcon(option.label, icons) : null;
14
+ const label = CommonService.capitalizeFirstLetter(showSeatDegree ? CommonService.addSeatDegree(option.label) : option.label);
13
15
  return (React.createElement("div", { key: option.id, onClick: () => onToggle(option.id), className: "flex items-center justify-between py-[12px] px-[2px] cursor-pointer text-[13px] text-[#464647] select-none [-webkit-tap-highlight-color:transparent]" },
14
16
  kind === "rating" ? (React.createElement(RatingLabel, { stars: option.stars, label: option.label, starColor: starColor, icons: icons })) : option.icon ? (React.createElement("div", { className: "flex items-center gap-[10px]" },
15
17
  React.createElement(TimeOfDayIcon, { variant: option.icon, size: 16, src: (_a = icons === null || icons === void 0 ? void 0 : icons.timeOfDayIcons) === null || _a === void 0 ? void 0 : _a[option.icon] }),
16
- React.createElement("span", null, option.label))) : (React.createElement("span", null, CommonService.capitalizeFirstLetter(option.label))),
18
+ React.createElement("span", null, option.label))) : seatIcon ? (React.createElement("div", { className: "flex items-center gap-[10px]" },
19
+ React.createElement("img", { src: seatIcon, alt: "seattype", className: "w-[16px] h-[16px]" }),
20
+ React.createElement("span", null, label))) : (React.createElement("span", null, label)),
17
21
  React.createElement(CheckboxIcon, { checked: option.selected, color: primaryColor, spriteSrc: icons === null || icons === void 0 ? void 0 : icons.checkBoxIcon })));
18
22
  })));
19
23
  };
@@ -77,6 +77,11 @@ export interface FilterSortBarIcons {
77
77
  timeOfDayIcons?: Partial<Record<TimeOfDayVariant, string>>;
78
78
  /** Icons for sort chips whose key doesn't already map to a category/time-of-day icon (e.g. "duration" for a Rápido chip). Keyed by SortOption.key. */
79
79
  sortIcons?: Partial<Record<string, string>>;
80
+ /** Seat-type icons shown next to "Tipo de asiento" options whose label contains 120/140/160/180, only when isShowSeatDegree is true. */
81
+ seat120?: string;
82
+ seat140?: string;
83
+ seat160?: string;
84
+ seat180?: string;
80
85
  }
81
86
  export interface FilterSortBarMobileProps {
82
87
  categories: FilterCategory[];
@@ -88,4 +93,6 @@ export interface FilterSortBarMobileProps {
88
93
  currencySign?: string;
89
94
  colors?: FilterSortBarColors;
90
95
  icons?: FilterSortBarIcons;
96
+ /** In the Filtros sheet's seat-type tab, adds "°" to 120/140/160/180 labels and shows icons.seat120/140/160/180 next to them. */
97
+ isShowSeatDegree?: boolean;
91
98
  }
@@ -86,7 +86,7 @@ function getSeatLabelIcon(label, serviceItem) {
86
86
  function addSeatDegree(label) {
87
87
  if (!label)
88
88
  return label;
89
- return String(label).replace(/\b(140|160|180)\b(?!°)/g, "$1°");
89
+ return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
90
90
  }
91
91
  function PeruSeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, serviceItem, renderIcon, discountSeatPriceColor, isSeatIcon, isShowSeatIcon, }) {
92
92
  var _a;
@@ -94,7 +94,7 @@ function getSeatLabelIcon(label, serviceItem) {
94
94
  function addSeatDegree(label) {
95
95
  if (!label)
96
96
  return label;
97
- return String(label).replace(/\b(140|160|180)\b(?!°)/g, "$1°");
97
+ return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
98
98
  }
99
99
  function SeatSection({ seatTypes, availableSeats, isSoldOut, priceColor, currencySign, removeDuplicateSeats, selectedSeatKey, onSeatSelect, isPeru, serviceItem, renderIcon, dpSeatColor, discountSeatPriceColor, isSeatIcon, isTrain, topLabelColor, tooltipColor, discountSeatCrossPriceColor, isShowSeatIcon }) {
100
100
  var _a;
@@ -42,7 +42,7 @@ function getSeatLabelIcon(label, serviceItem) {
42
42
  function addSeatDegree(label) {
43
43
  if (!label)
44
44
  return label;
45
- return String(label).replace(/\b(140|160|180)\b(?!°)/g, "$1°");
45
+ return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
46
46
  }
47
47
  function hasInclinationSeatTypes(seatTypes) {
48
48
  return (seatTypes !== null && seatTypes !== void 0 ? seatTypes : []).some((seat) => { var _a; return String((_a = seat === null || seat === void 0 ? void 0 : seat.label) !== null && _a !== void 0 ? _a : "").includes("°"); });
@@ -14,6 +14,8 @@ declare const commonService: {
14
14
  capitalizeFirstLetter: (str: any) => string;
15
15
  formatTimeLabel: (label: string) => string;
16
16
  formatDefaultLabel: (label: string) => string;
17
+ addSeatDegree: (label: string) => string;
18
+ getSeatDegreeIcon: (label: string, icons: any) => string | null;
17
19
  calculateDiscountedPrice: (originalPrice: number | string, serviceItem?: {
18
20
  discount_type?: string;
19
21
  discount_value?: number;
@@ -323,6 +323,15 @@ const commonService = {
323
323
  return "";
324
324
  return label.toLowerCase();
325
325
  },
326
+ addSeatDegree: (label) => {
327
+ if (!label)
328
+ return label;
329
+ return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
330
+ },
331
+ getSeatDegreeIcon: (label, icons) => {
332
+ const match = String(label !== null && label !== void 0 ? label : "").match(/\b(120|140|160|180)\b/);
333
+ return match ? (icons === null || icons === void 0 ? void 0 : icons[`seat${match[1]}`]) || null : null;
334
+ },
326
335
  calculateDiscountedPrice: (originalPrice, serviceItem) => {
327
336
  var _a;
328
337
  const price = typeof originalPrice === "string"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kupos-ui-components-lib",
3
- "version": "10.4.58",
3
+ "version": "10.4.60",
4
4
  "description": "A reusable UI components package",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -25,6 +25,7 @@ const FilterBarDesktop = ({
25
25
  isTrain,
26
26
  isPeru,
27
27
  hideOperator,
28
+ isShowSeatDegree,
28
29
  setCoachLoading,
29
30
  setCoachKey,
30
31
  isAllinBus,
@@ -329,6 +330,7 @@ const FilterBarDesktop = ({
329
330
  translation={translation}
330
331
  isTrain={isTrain}
331
332
  hideOperator={hideOperator}
333
+ isShowSeatDegree={isShowSeatDegree}
332
334
  t={t}
333
335
  />
334
336
  );
@@ -302,6 +302,11 @@ class ServiceFilter extends React.Component<
302
302
  : val.label;
303
303
  }
304
304
 
305
+ const seatDegreeIcon =
306
+ option.type === "seat_types" && this.props.isShowSeatDegree
307
+ ? commonService.getSeatDegreeIcon(label, this.props.icons)
308
+ : null;
309
+
305
310
  return (
306
311
  <div
307
312
  key={i}
@@ -337,6 +342,13 @@ class ServiceFilter extends React.Component<
337
342
  className="w-[20px] h-[20px] mr-[10px] "
338
343
  />
339
344
  )}
345
+ {seatDegreeIcon && (
346
+ <img
347
+ src={seatDegreeIcon}
348
+ alt="seattype"
349
+ className="w-[20px] h-[20px] mr-[10px]"
350
+ />
351
+ )}
340
352
  <span
341
353
  style={{
342
354
  textTransform:
@@ -345,7 +357,12 @@ class ServiceFilter extends React.Component<
345
357
  >
346
358
  {option.type === "time"
347
359
  ? commonService.formatTimeLabel(label)
348
- : commonService.formatDefaultLabel(label)}
360
+ : option.type === "seat_types" &&
361
+ this.props.isShowSeatDegree
362
+ ? commonService.addSeatDegree(
363
+ commonService.formatDefaultLabel(label),
364
+ )
365
+ : commonService.formatDefaultLabel(label)}
349
366
  </span>
350
367
  </div>
351
368
  );
@@ -9,5 +9,6 @@ export interface ServiceFilterProps {
9
9
  translation: any;
10
10
  isTrain?: boolean;
11
11
  hideOperator?: boolean;
12
+ isShowSeatDegree?: boolean;
12
13
  t?: (key: string) => string;
13
14
  }
@@ -14,6 +14,7 @@ export interface FilterBarProps {
14
14
  busTerminals?: any;
15
15
  setTypes?: any;
16
16
  hideOperator?: boolean;
17
+ isShowSeatDegree?: boolean;
17
18
  colors: {
18
19
  selectedColor?: string;
19
20
  selectedTextColor?: string;
@@ -28,6 +29,11 @@ export interface FilterBarProps {
28
29
  iconSunsetOrange?: string;
29
30
  iconNight?: string;
30
31
  iconNightOrange?: string;
32
+ seat120?: string;
33
+ seat140?: string;
34
+ seat160?: string;
35
+ seat180?: string;
36
+ fallBackSeatIcon?: string;
31
37
  };
32
38
  translation?: { [key: string]: string };
33
39
  isTrain?: boolean;
@@ -59,6 +59,7 @@ const FilterSortBarMobile = ({
59
59
  currencySign = "$",
60
60
  colors,
61
61
  icons,
62
+ isShowSeatDegree = false,
62
63
  }: FilterSortBarMobileProps) => {
63
64
  const [openSheet, setOpenSheet] = useState<FilterCategoryKey | "all" | null>(null);
64
65
  const [isScrolled, setIsScrolled] = useState(false);
@@ -284,6 +285,7 @@ const FilterSortBarMobile = ({
284
285
  currencySign={currencySign}
285
286
  colors={resolvedColors}
286
287
  icons={resolvedIcons}
288
+ isShowSeatDegree={isShowSeatDegree}
287
289
  />
288
290
  </div>
289
291
  );
@@ -19,6 +19,7 @@ interface FiltersMasterSheetProps {
19
19
  currencySign: string;
20
20
  colors: Required<FilterSortBarColors>;
21
21
  icons?: FilterSortBarIcons;
22
+ isShowSeatDegree?: boolean;
22
23
  }
23
24
 
24
25
  const FiltersMasterSheet = ({
@@ -33,6 +34,7 @@ const FiltersMasterSheet = ({
33
34
  currencySign,
34
35
  colors,
35
36
  icons,
37
+ isShowSeatDegree,
36
38
  }: FiltersMasterSheetProps) => {
37
39
  const [activeKey, setActiveKey] = useState<FilterCategoryKey>(initialCategoryKey ?? categories[0]?.key ?? "time");
38
40
 
@@ -108,6 +110,7 @@ const FiltersMasterSheet = ({
108
110
  primaryColor={colors.primaryColor}
109
111
  starColor={colors.buttonColor || colors.primaryColor}
110
112
  icons={icons}
113
+ showSeatDegree={!!isShowSeatDegree && activeCategory?.key === "seatType"}
111
114
  />
112
115
  )}
113
116
  </div>
@@ -11,6 +11,8 @@ interface OptionCheckboxListProps {
11
11
  /** Filled-star color for rating options — the app's button/CTA color, not the chip accent. */
12
12
  starColor: string;
13
13
  icons?: FilterSortBarIcons;
14
+ /** For options whose label contains 120/140/160/180: add "°" and show icons.seat120/140/160/180. */
15
+ showSeatDegree?: boolean;
14
16
  }
15
17
 
16
18
  const RatingLabel = ({
@@ -39,7 +41,15 @@ const RatingLabel = ({
39
41
  </div>
40
42
  );
41
43
 
42
- const OptionCheckboxList = ({ options, kind, onToggle, primaryColor, starColor, icons }: OptionCheckboxListProps) => {
44
+ const OptionCheckboxList = ({
45
+ options,
46
+ kind,
47
+ onToggle,
48
+ primaryColor,
49
+ starColor,
50
+ icons,
51
+ showSeatDegree,
52
+ }: OptionCheckboxListProps) => {
43
53
  if (!options?.length) {
44
54
  return (
45
55
  <div className="py-[24px] text-center text-[#9B9B9B] text-[13px]">
@@ -50,25 +60,37 @@ const OptionCheckboxList = ({ options, kind, onToggle, primaryColor, starColor,
50
60
 
51
61
  return (
52
62
  <div>
53
- {options.map((option) => (
54
- <div
55
- key={option.id}
56
- onClick={() => onToggle(option.id)}
57
- className="flex items-center justify-between py-[12px] px-[2px] cursor-pointer text-[13px] text-[#464647] select-none [-webkit-tap-highlight-color:transparent]"
58
- >
59
- {kind === "rating" ? (
60
- <RatingLabel stars={option.stars} label={option.label} starColor={starColor} icons={icons} />
61
- ) : option.icon ? (
62
- <div className="flex items-center gap-[10px]">
63
- <TimeOfDayIcon variant={option.icon} size={16} src={icons?.timeOfDayIcons?.[option.icon]} />
64
- <span>{option.label}</span>
65
- </div>
66
- ) : (
67
- <span>{CommonService.capitalizeFirstLetter(option.label)}</span>
68
- )}
69
- <CheckboxIcon checked={option.selected} color={primaryColor} spriteSrc={icons?.checkBoxIcon} />
70
- </div>
71
- ))}
63
+ {options.map((option) => {
64
+ const seatIcon = showSeatDegree ? CommonService.getSeatDegreeIcon(option.label, icons) : null;
65
+ const label = CommonService.capitalizeFirstLetter(
66
+ showSeatDegree ? CommonService.addSeatDegree(option.label) : option.label,
67
+ );
68
+
69
+ return (
70
+ <div
71
+ key={option.id}
72
+ onClick={() => onToggle(option.id)}
73
+ className="flex items-center justify-between py-[12px] px-[2px] cursor-pointer text-[13px] text-[#464647] select-none [-webkit-tap-highlight-color:transparent]"
74
+ >
75
+ {kind === "rating" ? (
76
+ <RatingLabel stars={option.stars} label={option.label} starColor={starColor} icons={icons} />
77
+ ) : option.icon ? (
78
+ <div className="flex items-center gap-[10px]">
79
+ <TimeOfDayIcon variant={option.icon} size={16} src={icons?.timeOfDayIcons?.[option.icon]} />
80
+ <span>{option.label}</span>
81
+ </div>
82
+ ) : seatIcon ? (
83
+ <div className="flex items-center gap-[10px]">
84
+ <img src={seatIcon} alt="seattype" className="w-[16px] h-[16px]" />
85
+ <span>{label}</span>
86
+ </div>
87
+ ) : (
88
+ <span>{label}</span>
89
+ )}
90
+ <CheckboxIcon checked={option.selected} color={primaryColor} spriteSrc={icons?.checkBoxIcon} />
91
+ </div>
92
+ );
93
+ })}
72
94
  </div>
73
95
  );
74
96
  };
@@ -100,6 +100,11 @@ export interface FilterSortBarIcons {
100
100
  timeOfDayIcons?: Partial<Record<TimeOfDayVariant, string>>;
101
101
  /** Icons for sort chips whose key doesn't already map to a category/time-of-day icon (e.g. "duration" for a Rápido chip). Keyed by SortOption.key. */
102
102
  sortIcons?: Partial<Record<string, string>>;
103
+ /** Seat-type icons shown next to "Tipo de asiento" options whose label contains 120/140/160/180, only when isShowSeatDegree is true. */
104
+ seat120?: string;
105
+ seat140?: string;
106
+ seat160?: string;
107
+ seat180?: string;
103
108
  }
104
109
 
105
110
  export interface FilterSortBarMobileProps {
@@ -112,4 +117,6 @@ export interface FilterSortBarMobileProps {
112
117
  currencySign?: string;
113
118
  colors?: FilterSortBarColors;
114
119
  icons?: FilterSortBarIcons;
120
+ /** In the Filtros sheet's seat-type tab, adds "°" to 120/140/160/180 labels and shows icons.seat120/140/160/180 next to them. */
121
+ isShowSeatDegree?: boolean;
115
122
  }
@@ -120,7 +120,7 @@ function getSeatLabelIcon(label: string, serviceItem: any) {
120
120
 
121
121
  function addSeatDegree(label: string) {
122
122
  if (!label) return label;
123
- return String(label).replace(/\b(140|160|180)\b(?!°)/g, "$1°");
123
+ return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
124
124
  }
125
125
 
126
126
  function PeruSeatSection({
@@ -140,7 +140,7 @@ function getSeatLabelIcon(label: string, serviceItem: any) {
140
140
 
141
141
  function addSeatDegree(label: string) {
142
142
  if (!label) return label;
143
- return String(label).replace(/\b(140|160|180)\b(?!°)/g, "$1°");
143
+ return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
144
144
  }
145
145
 
146
146
  function SeatSection({
@@ -106,7 +106,7 @@ function getSeatLabelIcon(label: string, serviceItem: any) {
106
106
 
107
107
  function addSeatDegree(label: string | null) {
108
108
  if (!label) return label;
109
- return String(label).replace(/\b(140|160|180)\b(?!°)/g, "$1°");
109
+ return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
110
110
  }
111
111
 
112
112
  function hasInclinationSeatTypes(seatTypes: SeatType[]): boolean {
@@ -339,6 +339,16 @@ const commonService = {
339
339
  return label.toLowerCase();
340
340
  },
341
341
 
342
+ addSeatDegree: (label: string) => {
343
+ if (!label) return label;
344
+ return String(label).replace(/\b(120|140|160|180)\b(?!°)/g, "$1°");
345
+ },
346
+
347
+ getSeatDegreeIcon: (label: string, icons: any): string | null => {
348
+ const match = String(label ?? "").match(/\b(120|140|160|180)\b/);
349
+ return match ? icons?.[`seat${match[1]}`] || null : null;
350
+ },
351
+
342
352
  calculateDiscountedPrice: (
343
353
  originalPrice: number | string,
344
354
  serviceItem?: {