mautourco-components 0.2.19 → 0.2.21

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 (109) hide show
  1. package/dist/components/atoms/Icon/icons/BusIcon.js +1 -1
  2. package/dist/components/atoms/Icon/icons/CatamaranIcon.js +1 -1
  3. package/dist/components/atoms/Icon/icons/ClockIcon.d.ts +4 -0
  4. package/dist/components/atoms/Icon/icons/ClockIcon.js +36 -0
  5. package/dist/components/atoms/Icon/icons/CloseCircleIcon.js +1 -1
  6. package/dist/components/atoms/Icon/icons/MapIcon.js +1 -1
  7. package/dist/components/atoms/Icon/icons/PlusCircleIcon.js +1 -1
  8. package/dist/components/atoms/Icon/icons/SeaIcon.js +1 -1
  9. package/dist/components/atoms/Icon/icons/registry.d.ts +1 -0
  10. package/dist/components/atoms/Icon/icons/registry.js +2 -0
  11. package/dist/components/atoms/Inputs/Input/Input.d.ts +2 -1
  12. package/dist/components/atoms/Inputs/Input/Input.js +1 -1
  13. package/dist/components/atoms/Inputs/Textarea/Textarea.d.ts +3 -1
  14. package/dist/components/atoms/Inputs/Textarea/Textarea.js +7 -5
  15. package/dist/components/molecules/BookingPax/BookingPax.d.ts +7 -0
  16. package/dist/components/molecules/BookingPax/BookingPax.js +21 -0
  17. package/dist/components/molecules/BookingPax/BookingPaxAccom.d.ts +22 -0
  18. package/dist/components/molecules/BookingPax/BookingPaxAccom.js +61 -0
  19. package/dist/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.css +2090 -0
  20. package/dist/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.d.ts +31 -0
  21. package/dist/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.js +96 -0
  22. package/dist/components/molecules/BookingPax/BookingPaxExcursion.d.ts +14 -0
  23. package/dist/components/molecules/BookingPax/BookingPaxExcursion.js +31 -0
  24. package/dist/components/molecules/BookingPax/BookingPaxHeader.d.ts +16 -0
  25. package/dist/components/molecules/BookingPax/BookingPaxHeader.js +28 -0
  26. package/dist/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.css +2103 -0
  27. package/dist/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.d.ts +11 -0
  28. package/dist/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.js +19 -0
  29. package/dist/components/molecules/BookingPax/BookingPaxRemarks.d.ts +5 -0
  30. package/dist/components/molecules/BookingPax/BookingPaxRemarks.js +37 -0
  31. package/dist/components/molecules/BookingPax/BookingPaxTransfer.d.ts +18 -0
  32. package/dist/components/molecules/BookingPax/BookingPaxTransfer.js +40 -0
  33. package/dist/components/molecules/BookingPax/index.d.ts +5 -0
  34. package/dist/components/molecules/BookingPax/index.js +1 -0
  35. package/dist/components/molecules/Calendar/CalendarInput.d.ts +6 -3
  36. package/dist/components/molecules/Calendar/CalendarInput.js +10 -10
  37. package/dist/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.css +2142 -0
  38. package/dist/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.d.ts +11 -0
  39. package/dist/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.js +19 -0
  40. package/dist/components/molecules/DialogContentPolicy/DialogCancellationAccom.d.ts +9 -0
  41. package/dist/components/molecules/DialogContentPolicy/DialogCancellationAccom.js +24 -0
  42. package/dist/components/molecules/DialogContentPolicy/DialogCancellationExcursion.d.ts +17 -0
  43. package/dist/components/molecules/DialogContentPolicy/DialogCancellationExcursion.js +20 -0
  44. package/dist/components/molecules/DialogContentPolicy/DialogCancellationList.d.ts +11 -0
  45. package/dist/components/molecules/DialogContentPolicy/DialogCancellationList.js +33 -0
  46. package/dist/components/molecules/DialogContentPolicy/DialogContentPolicy.d.ts +11 -0
  47. package/dist/components/molecules/DialogContentPolicy/DialogContentPolicy.js +27 -0
  48. package/dist/components/molecules/DialogContentPolicy/index.d.ts +4 -0
  49. package/dist/components/molecules/DialogContentPolicy/index.js +1 -0
  50. package/dist/components/molecules/ServiceTitle/ServiceTitle.css +3 -0
  51. package/dist/components/molecules/ServiceTitle/ServiceTitle.d.ts +7 -1
  52. package/dist/components/molecules/ServiceTitle/ServiceTitle.js +4 -3
  53. package/dist/components/organisms/Booking/Booking.d.ts +2 -0
  54. package/dist/components/organisms/Booking/Booking.js +4 -0
  55. package/dist/components/organisms/Booking/BookingHeader.d.ts +8 -0
  56. package/dist/components/organisms/Booking/BookingHeader.js +17 -0
  57. package/dist/components/organisms/Booking/BookingPaxList.d.ts +25 -0
  58. package/dist/components/organisms/Booking/BookingPaxList.js +117 -0
  59. package/dist/components/organisms/Booking/BookingStep/BookingStep.d.ts +1 -0
  60. package/dist/components/organisms/Booking/BookingStep/BookingStep.js +5 -2
  61. package/dist/components/organisms/DateTimePicker/DateTimePicker.d.ts +6 -3
  62. package/dist/components/organisms/DateTimePicker/DateTimePicker.js +28 -22
  63. package/dist/components/organisms/DialogBookingConfirm/DialogBookingConfirm.d.ts +10 -0
  64. package/dist/components/organisms/DialogBookingConfirm/DialogBookingConfirm.js +17 -0
  65. package/dist/components/ui/checkbox.d.ts +4 -0
  66. package/dist/components/ui/checkbox.js +31 -0
  67. package/dist/hooks/useBookingPax.d.ts +8 -0
  68. package/dist/hooks/useBookingPax.js +43 -0
  69. package/dist/index.d.ts +1 -0
  70. package/dist/index.js +1 -0
  71. package/package.json +2 -1
  72. package/src/components/atoms/Icon/icons/BusIcon.tsx +1 -1
  73. package/src/components/atoms/Icon/icons/CatamaranIcon.tsx +1 -1
  74. package/src/components/atoms/Icon/icons/ClockIcon.tsx +46 -0
  75. package/src/components/atoms/Icon/icons/CloseCircleIcon.tsx +1 -1
  76. package/src/components/atoms/Icon/icons/MapIcon.tsx +6 -2
  77. package/src/components/atoms/Icon/icons/PlusCircleIcon.tsx +1 -1
  78. package/src/components/atoms/Icon/icons/SeaIcon.tsx +1 -1
  79. package/src/components/atoms/Icon/icons/registry.tsx +2 -0
  80. package/src/components/atoms/Inputs/Input/Input.tsx +6 -5
  81. package/src/components/atoms/Inputs/Textarea/Textarea.tsx +18 -4
  82. package/src/components/molecules/BookingPax/BookingPax.tsx +12 -0
  83. package/src/components/molecules/BookingPax/BookingPaxAccom.tsx +120 -0
  84. package/src/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.css +4 -0
  85. package/src/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.tsx +188 -0
  86. package/src/components/molecules/BookingPax/BookingPaxExcursion.tsx +77 -0
  87. package/src/components/molecules/BookingPax/BookingPaxHeader.tsx +47 -0
  88. package/src/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.css +15 -0
  89. package/src/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.tsx +25 -0
  90. package/src/components/molecules/BookingPax/BookingPaxRemarks.tsx +46 -0
  91. package/src/components/molecules/BookingPax/BookingPaxTransfer.tsx +121 -0
  92. package/src/components/molecules/BookingPax/index.ts +9 -0
  93. package/src/components/molecules/Calendar/CalendarInput.tsx +26 -24
  94. package/src/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.css +37 -0
  95. package/src/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.tsx +28 -0
  96. package/src/components/molecules/DialogContentPolicy/DialogCancellationAccom.tsx +65 -0
  97. package/src/components/molecules/DialogContentPolicy/DialogCancellationExcursion.tsx +59 -0
  98. package/src/components/molecules/DialogContentPolicy/DialogCancellationList.tsx +49 -0
  99. package/src/components/molecules/DialogContentPolicy/DialogContentPolicy.tsx +45 -0
  100. package/src/components/molecules/DialogContentPolicy/index.ts +5 -0
  101. package/src/components/molecules/ServiceTitle/ServiceTitle.css +1 -1
  102. package/src/components/molecules/ServiceTitle/ServiceTitle.tsx +25 -7
  103. package/src/components/organisms/Booking/Booking.tsx +4 -0
  104. package/src/components/organisms/Booking/BookingHeader.tsx +24 -0
  105. package/src/components/organisms/Booking/BookingPaxList.tsx +224 -0
  106. package/src/components/organisms/Booking/BookingStep/BookingStep.tsx +8 -2
  107. package/src/components/organisms/DateTimePicker/DateTimePicker.tsx +69 -49
  108. package/src/components/organisms/DialogBookingConfirm/DialogBookingConfirm.tsx +25 -0
  109. package/src/components/ui/checkbox.tsx +32 -0
@@ -0,0 +1,28 @@
1
+ import { IconName } from '@/src/components/atoms/Icon/Icon';
2
+ import { cn } from '@/src/lib/utils';
3
+ import React from 'react';
4
+ import { ServiceTitle } from '../../ServiceTitle/ServiceTitle';
5
+ import './CancellationLayout.css';
6
+
7
+ export interface CancellationLayoutProps {
8
+ title: React.ReactNode;
9
+ icon: IconName;
10
+ children?: React.ReactNode;
11
+ className?: string;
12
+ image: string;
13
+ }
14
+
15
+ export const CancellationLayout: React.FC<CancellationLayoutProps> = (props) => {
16
+ const { title, icon, children, className, image } = props;
17
+ return (
18
+ <div className={cn('cancellation-layout', className)}>
19
+ <ServiceTitle title={title} icon={icon} />
20
+ <div className="cancellation-layout__content">
21
+ <div className="cancellation-layout__content-left">{children}</div>
22
+ <figure className="cancellation-layout__content-right">
23
+ <img src={image} alt={title?.toString() || ''} width={150} height={200} />
24
+ </figure>
25
+ </div>
26
+ </div>
27
+ );
28
+ };
@@ -0,0 +1,65 @@
1
+ import useStays from '@/src/hooks/useStays';
2
+ import { RoomItem } from '@/src/types/table';
3
+ import { Fragment } from 'react';
4
+ import Chip from '../../atoms/Chip/Chip';
5
+ import { Text } from '../../atoms/Typography/Typography';
6
+ import { DateDisplay } from '../DateDisplay/DateDisplay';
7
+ import TextWithIcon from '../TextWithIcon/TextWithIcon';
8
+ import { CancellationLayout } from './CancellationLayout/CancellationLayout';
9
+
10
+ export interface DialogCancellationAccomProps {
11
+ image: string;
12
+ hotelName: string;
13
+ dates: string[];
14
+ rooms: RoomItem[];
15
+ }
16
+
17
+ export const DialogCancellationAccom: React.FC<DialogCancellationAccomProps> = (
18
+ props
19
+ ) => {
20
+ const { image, hotelName, dates, rooms } = props;
21
+ const stay = useStays(dates);
22
+
23
+ return (
24
+ <CancellationLayout title="Accommodation" icon="accom" image={image}>
25
+ <div className="space-y-6">
26
+ <div className="space-y-1">
27
+ <Text variant="bold">{hotelName}</Text>
28
+ <DateDisplay dates={dates} />
29
+ <TextWithIcon icon="night" textLeading="4">
30
+ {stay} Nights
31
+ </TextWithIcon>
32
+ </div>
33
+ <div className="">
34
+ {rooms.map((room, index) => (
35
+ <div key={`rm-${index}`} className="space-y-4">
36
+ <div className="space-y-1">
37
+ <TextWithIcon icon="accom" textLeading="4">
38
+ {index + 1} Room
39
+ </TextWithIcon>
40
+ <Text size="sm">{room.RoomName}</Text>
41
+ </div>
42
+ {room.cancellation_policy.map((policy) => (
43
+ <Fragment key={`policy-${index}`}>
44
+ <Chip type="outline" color="brand" isBlackText>
45
+ Policy period applies{' '}
46
+ <DateDisplay
47
+ dates={[policy.ValidFrom, policy.ValidTo]}
48
+ textSize="xs"
49
+ />
50
+ </Chip>
51
+ <Text variant="bold" size="xs" leading="4">
52
+ {policy.Value} % of total price
53
+ </Text>
54
+ <Text size="xs" leading="4">
55
+ {policy.Description}
56
+ </Text>
57
+ </Fragment>
58
+ ))}
59
+ </div>
60
+ ))}
61
+ </div>
62
+ </div>
63
+ </CancellationLayout>
64
+ );
65
+ };
@@ -0,0 +1,59 @@
1
+ import { Text } from '../../atoms/Typography/Typography';
2
+ import { DateDisplay } from '../DateDisplay/DateDisplay';
3
+ import { ServiceInfo } from '../ServiceInfo/ServiceInfo';
4
+ import { CancellationLayout } from './CancellationLayout/CancellationLayout';
5
+
6
+ export interface DialogCancellationExcursionProps {
7
+ image: string;
8
+ excursionName: string;
9
+ date: string;
10
+ policy: {
11
+ title: string;
12
+ description: string;
13
+ };
14
+ details: {
15
+ duration: string;
16
+ meal: string;
17
+ accessibility: string;
18
+ transferType: string;
19
+ };
20
+ }
21
+
22
+ export const DialogCancellationExcursion: React.FC<DialogCancellationExcursionProps> = (
23
+ props
24
+ ) => {
25
+ const { image, excursionName, date, details, policy } = props;
26
+
27
+ return (
28
+ <CancellationLayout title="Excursion" icon="map" image={image}>
29
+ <div className="space-y-6">
30
+ <div className="space-y-1">
31
+ <Text variant="bold">{excursionName}</Text>
32
+ <DateDisplay dates={[date]} />
33
+ </div>
34
+ <ServiceInfo>
35
+ <ServiceInfo.Item icon="stopwatch" label="Duration">
36
+ {details.duration}
37
+ </ServiceInfo.Item>
38
+ <ServiceInfo.Item icon="utensils" label="Meal">
39
+ {details.meal}
40
+ </ServiceInfo.Item>
41
+ <ServiceInfo.Item icon="bike" label="Difficulty">
42
+ {details.accessibility}
43
+ </ServiceInfo.Item>
44
+ <ServiceInfo.Item icon="car" label="Transfer Type">
45
+ {details.transferType}
46
+ </ServiceInfo.Item>
47
+ </ServiceInfo>
48
+ <div className="">
49
+ <Text variant="bold" size="xs" leading="4">
50
+ {policy.title}
51
+ </Text>
52
+ <Text size="xs" leading="4">
53
+ {policy.description}
54
+ </Text>
55
+ </div>
56
+ </div>
57
+ </CancellationLayout>
58
+ );
59
+ };
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import {
3
+ DialogCancellationAccom,
4
+ DialogCancellationAccomProps,
5
+ } from './DialogCancellationAccom';
6
+ import {
7
+ DialogCancellationExcursion,
8
+ DialogCancellationExcursionProps,
9
+ } from './DialogCancellationExcursion';
10
+ import { DialogContentPolicy } from './DialogContentPolicy';
11
+
12
+ export interface CancellationItem {
13
+ type: 'accommodation' | 'excursion';
14
+ data: DialogCancellationAccomProps | DialogCancellationExcursionProps;
15
+ }
16
+
17
+ export interface DialogCancellationListProps {
18
+ cancellations: CancellationItem[];
19
+ }
20
+
21
+ export const DialogCancellationList: React.FC<DialogCancellationListProps> = (props) => {
22
+ const { cancellations } = props;
23
+
24
+ const isAccommodation = (
25
+ data: CancellationItem['data']
26
+ ): data is DialogCancellationAccomProps => {
27
+ return 'hotelName' in data;
28
+ };
29
+
30
+ const isExcursion = (
31
+ data: CancellationItem['data']
32
+ ): data is DialogCancellationExcursionProps => {
33
+ return 'excursionName' in data;
34
+ };
35
+
36
+ return (
37
+ <DialogContentPolicy>
38
+ {cancellations.map((c, index) => {
39
+ if (c.type === 'accommodation' && isAccommodation(c.data)) {
40
+ return <DialogCancellationAccom key={`acc-${index}`} {...c.data} />;
41
+ }
42
+ if (c.type === 'excursion' && isExcursion(c.data)) {
43
+ return <DialogCancellationExcursion key={`exc-${index}`} {...c.data} />;
44
+ }
45
+ return null;
46
+ })}
47
+ </DialogContentPolicy>
48
+ );
49
+ };
@@ -0,0 +1,45 @@
1
+ import { CheckedState } from '@radix-ui/react-checkbox';
2
+ import React, { useState } from 'react';
3
+ import Button from '../../atoms/Button/Button';
4
+ import { Text } from '../../atoms/Typography/Typography';
5
+ import { Checkbox } from '../../ui/checkbox';
6
+ import { DialogCancellationAccom } from './DialogCancellationAccom';
7
+ import { DialogCancellationExcursion } from './DialogCancellationExcursion';
8
+ import { DialogCancellationList } from './DialogCancellationList';
9
+
10
+ export interface DialogContentPolicyProps {
11
+ children: React.ReactNode;
12
+ onSubmit?: () => void;
13
+ }
14
+
15
+ export const DialogContentPolicy = (props: DialogContentPolicyProps) => {
16
+ const { children, onSubmit } = props;
17
+ const [accepted, setAccepted] = useState<CheckedState>(false);
18
+
19
+ return (
20
+ <div className="cancellation-policy">
21
+ {children}
22
+ <div className="flex justify-end gap-x-8 py-9">
23
+ <div className="flex gap-x-2.5 items-center">
24
+ <label htmlFor="accept-terms">
25
+ <Text size="sm" as="span">
26
+ I've read and accept the cancellation policy.
27
+ </Text>
28
+ </label>
29
+ <Checkbox id="accept-terms" checked={accepted} onCheckedChange={setAccepted} />
30
+ </div>
31
+ <Button
32
+ className="w-[250px]"
33
+ variant="secondary"
34
+ disabled={!accepted}
35
+ onClick={onSubmit}>
36
+ Next
37
+ </Button>
38
+ </div>
39
+ </div>
40
+ );
41
+ };
42
+
43
+ DialogContentPolicy.Accommodation = DialogCancellationAccom;
44
+ DialogContentPolicy.Excursion = DialogCancellationExcursion;
45
+ DialogContentPolicy.List = DialogCancellationList;
@@ -0,0 +1,5 @@
1
+ export * from './DialogContentPolicy';
2
+
3
+ export type { CancellationLayoutProps } from './CancellationLayout/CancellationLayout';
4
+ export type { DialogCancellationAccomProps } from './DialogCancellationAccom';
5
+ export type { DialogCancellationExcursionProps } from './DialogCancellationExcursion';
@@ -1,5 +1,5 @@
1
1
  .service-title {
2
- @apply relative;
2
+ @apply relative flex gap-x-2 items-center;
3
3
  padding-left: 10px;
4
4
  &::before {
5
5
  content: '';
@@ -1,19 +1,37 @@
1
1
  import React from 'react';
2
+ import Icon, { IconName } from '../../atoms/Icon/Icon';
3
+ import { IconBaseProps } from '../../atoms/Icon/icons/registry';
2
4
  import { Text, TextProps } from '../../atoms/Typography/Typography';
3
5
  import './ServiceTitle.css';
4
6
 
5
7
  export interface ServiceTitleProps {
6
- title: string;
8
+ title: React.ReactNode;
7
9
  textSize?: TextProps['size'];
10
+ textLeading?: TextProps['leading'];
11
+ textVariant?: TextProps['variant'];
12
+ icon?: IconName;
13
+ iconSize?: IconBaseProps['size'];
8
14
  }
9
15
 
10
16
  export const ServiceTitle: React.FC<ServiceTitleProps> = (props) => {
11
- const { title, textSize = 'md' } = props;
17
+ const {
18
+ title,
19
+ textSize = 'md',
20
+ icon,
21
+ iconSize = 'sm',
22
+ textLeading = '5',
23
+ textVariant = 'bold',
24
+ } = props;
12
25
  return (
13
- <div className="service-title">
14
- <Text variant="bold" size={textSize} color="accent">
15
- {title}
16
- </Text>
17
- </div>
26
+ <Text
27
+ className="service-title"
28
+ size={textSize}
29
+ variant={textVariant}
30
+ leading={textLeading}
31
+ as="div"
32
+ color="accent">
33
+ {icon && <Icon name={icon} size={iconSize} />}
34
+ <span>{title}</span>
35
+ </Text>
18
36
  );
19
37
  };
@@ -2,6 +2,8 @@ import { cn } from '@/src/lib/utils';
2
2
  import React from 'react';
3
3
  import './Booking.css';
4
4
  import { BookingDocket } from './BookingDocket/BookingDocket';
5
+ import { BookingHeader } from './BookingHeader';
6
+ import { BookingPaxList } from './BookingPaxList';
5
7
  import { BookingStep } from './BookingStep/BookingStep';
6
8
 
7
9
  export interface BookingProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -19,3 +21,5 @@ export default function Booking(props: BookingProps) {
19
21
 
20
22
  Booking.Docket = BookingDocket;
21
23
  Booking.Step = BookingStep;
24
+ Booking.Header = BookingHeader;
25
+ Booking.PaxList = BookingPaxList;
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { Heading, Text } from '../../atoms/Typography/Typography';
3
+
4
+ export interface BookingHeaderProps {
5
+ fileNumber: string | number;
6
+ reference: string | number;
7
+ location: string;
8
+ isBooking?: boolean;
9
+ }
10
+
11
+ export const BookingHeader: React.FC<BookingHeaderProps> = (props) => {
12
+ const { fileNumber, reference, isBooking, location } = props;
13
+ return (
14
+ <div className="space-y-2">
15
+ <Text color="light">File number: {fileNumber}</Text>
16
+ <Heading level={6} as="h1" color="brand">
17
+ {isBooking ? 'Booking' : 'Quotation'}: N° {reference}
18
+ </Heading>
19
+ <Text color="accent" size="xl">
20
+ {location}
21
+ </Text>
22
+ </div>
23
+ );
24
+ };
@@ -0,0 +1,224 @@
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import {
3
+ BookingPax,
4
+ BookingPaxAccomProps,
5
+ BookingPaxExcursionProps,
6
+ BookingPaxTransferProps,
7
+ } from '../../molecules/BookingPax';
8
+ import {
9
+ BookingPaxClientInfo,
10
+ BookingPaxClientOptions,
11
+ } from '../../molecules/BookingPax/BookingPaxClient/BookingPaxClient';
12
+
13
+ export interface BookingPaxData {
14
+ type: 'accommodation' | 'excursion' | 'transfer';
15
+ data:
16
+ | Omit<BookingPaxAccomProps, 'clientsInfo'>
17
+ | Omit<BookingPaxExcursionProps, 'clientsInfo'>
18
+ | Omit<BookingPaxTransferProps, 'clientsInfo'>;
19
+ }
20
+
21
+ export interface BookingPaxAutoFilledData {
22
+ type: BookingPaxData['type'];
23
+ parentIndex: number;
24
+ roomIndex?: number;
25
+ selectedClients: BookingPaxClientInfo[];
26
+ }
27
+
28
+ export interface BookingPaxProps {
29
+ data: BookingPaxData[];
30
+ clientsInfo: BookingPaxClientInfo[];
31
+ isSubmitted?: boolean;
32
+ onPaxChange?: (
33
+ index: number,
34
+ type: BookingPaxData['type'],
35
+ options: BookingPaxClientOptions
36
+ ) => void;
37
+ onRemarkChange?: (index: number, type: BookingPaxData['type'], value: string) => void;
38
+ onError?: (index: number, type: BookingPaxData['type'], hasError: boolean) => void;
39
+ onTimeChange?: (index: number, value: string | string[]) => void;
40
+ onFlightNumberChange?: (index: number, value: string) => void;
41
+ onAutoFilled?: (data: BookingPaxAutoFilledData[]) => void;
42
+ }
43
+
44
+ // Type guards for better type safety
45
+ const isAccommodationData = (
46
+ data: BookingPaxData['data']
47
+ ): data is BookingPaxAccomProps => {
48
+ return 'rooms' in data && 'hotelName' in data && 'roomName' in data;
49
+ };
50
+
51
+ const isExcursionData = (
52
+ data: BookingPaxData['data']
53
+ ): data is BookingPaxExcursionProps => {
54
+ return 'name' in data && 'type' in data && !('from' in data);
55
+ };
56
+
57
+ const isTransferData = (
58
+ data: BookingPaxData['data']
59
+ ): data is BookingPaxTransferProps => {
60
+ return 'from' in data && 'to' in data && 'type' in data;
61
+ };
62
+
63
+ export const BookingPaxList: React.FC<BookingPaxProps> = (props) => {
64
+ const {
65
+ data,
66
+ isSubmitted,
67
+ clientsInfo,
68
+ onPaxChange,
69
+ onRemarkChange,
70
+ onError,
71
+ onTimeChange,
72
+ onFlightNumberChange,
73
+ onAutoFilled,
74
+ } = props;
75
+
76
+ const [autoFilledData, setAutoFilledData] = useState<
77
+ {
78
+ type: BookingPaxData['type'];
79
+ parentIndex: number;
80
+ roomIndex?: number;
81
+ selectedClients: BookingPaxClientInfo[];
82
+ }[]
83
+ >([]);
84
+
85
+ // Memoized callback handlers to reduce re-renders
86
+ const handlePaxChange = useCallback(
87
+ (type: BookingPaxData['type'], index: number, options: BookingPaxClientOptions) => {
88
+ onPaxChange?.(index, type, options);
89
+ },
90
+ [onPaxChange]
91
+ );
92
+
93
+ const handleRemarkChange = useCallback(
94
+ (type: BookingPaxData['type'], index: number, value: string) => {
95
+ onRemarkChange?.(index, type, value);
96
+ },
97
+ [onRemarkChange]
98
+ );
99
+
100
+ const handleError = useCallback(
101
+ (index: number, type: BookingPaxData['type'], hasError: boolean) => {
102
+ onError?.(index, type, hasError);
103
+ },
104
+ [onError]
105
+ );
106
+
107
+ const handleTimeChange = useCallback(
108
+ (value: string | string[], index: number) => {
109
+ // Don't need type because only transfer has this callback
110
+ onTimeChange?.(index, value);
111
+ },
112
+ [onTimeChange]
113
+ );
114
+
115
+ const handleFlightNumberChange = useCallback(
116
+ (value: string, index: number) => {
117
+ // Don't need type because only transfer has this callback
118
+ onFlightNumberChange?.(index, value);
119
+ },
120
+ [onFlightNumberChange]
121
+ );
122
+
123
+ const handleAutoFilled = useCallback((type: BookingPaxData['type']) => {
124
+ return (paxCount: number, index: number, roomIndex?: number) => {
125
+ if (paxCount === clientsInfo.length) {
126
+ setAutoFilledData((prev) => {
127
+ const findParentIndex = prev.find((d) => d.parentIndex === index);
128
+ if (findParentIndex) {
129
+ return prev;
130
+ }
131
+ return [
132
+ ...prev,
133
+ {
134
+ type,
135
+ parentIndex: index,
136
+ selectedClients: clientsInfo,
137
+ roomIndex,
138
+ },
139
+ ];
140
+ });
141
+ }
142
+ };
143
+ }, []);
144
+
145
+ useEffect(() => {
146
+ data.forEach((item, index) => {
147
+ const { type, data: itemData } = item;
148
+ if (type === 'accommodation' && isAccommodationData(itemData)) {
149
+ const initAccom = handleAutoFilled('accommodation');
150
+ // if(itemData.clientsInfo.length === itemData)
151
+ itemData.rooms.forEach((room, roomIndex) => {
152
+ initAccom(room.paxCount, index, roomIndex);
153
+ });
154
+ } else if (type === 'excursion' && isExcursionData(itemData)) {
155
+ const initExcursion = handleAutoFilled('excursion');
156
+ initExcursion(itemData.paxCount, index);
157
+ } else if (type === 'transfer' && isTransferData(itemData)) {
158
+ const initTransfer = handleAutoFilled('transfer');
159
+ initTransfer(itemData.paxCount, index);
160
+ }
161
+ });
162
+ }, [data, handleAutoFilled]);
163
+
164
+ useEffect(() => {
165
+ onAutoFilled?.(autoFilledData);
166
+ }, [autoFilledData, onAutoFilled]);
167
+
168
+ return (
169
+ <BookingPax>
170
+ {data.map((item, index) => {
171
+ const { type, data: itemData } = item;
172
+
173
+ const callbacks = {
174
+ onPaxChange: (options: BookingPaxClientOptions) =>
175
+ handlePaxChange(type, index, options),
176
+ onRemarkChange: (value: string) => handleRemarkChange(type, index, value),
177
+ };
178
+
179
+ if (type === 'accommodation' && isAccommodationData(itemData)) {
180
+ return (
181
+ <BookingPax.Accom
182
+ key={`bp-accom-${index}`}
183
+ {...itemData}
184
+ {...callbacks}
185
+ clientsInfo={clientsInfo}
186
+ isSubmitted={isSubmitted}
187
+ onError={(roomIndex, hasError) => handleError(roomIndex, type, hasError)}
188
+ />
189
+ );
190
+ }
191
+
192
+ if (type === 'excursion' && isExcursionData(itemData)) {
193
+ return (
194
+ <BookingPax.Excursion
195
+ key={`bp-excursion-${index}`}
196
+ {...itemData}
197
+ clientsInfo={clientsInfo}
198
+ isSubmitted={isSubmitted}
199
+ onError={(hasError) => handleError(index, type, hasError)}
200
+ {...callbacks}
201
+ />
202
+ );
203
+ }
204
+
205
+ if (type === 'transfer' && isTransferData(itemData)) {
206
+ return (
207
+ <BookingPax.Transfer
208
+ key={`bp-transfer-${index}`}
209
+ {...itemData}
210
+ onTimeChange={(value) => handleTimeChange(value, index)}
211
+ onFlightNumberChange={(value) => handleFlightNumberChange(value, index)}
212
+ clientsInfo={clientsInfo}
213
+ isSubmitted={isSubmitted}
214
+ onError={(hasError) => handleError(index, type, hasError)}
215
+ {...callbacks}
216
+ />
217
+ );
218
+ }
219
+
220
+ return null;
221
+ })}
222
+ </BookingPax>
223
+ );
224
+ };
@@ -3,11 +3,12 @@ import {
3
3
  StepperTimeline,
4
4
  StepperTimelineItem,
5
5
  } from '@/src/components/molecules/StepperTimeline/StepperTimeline';
6
- import React, { useState } from 'react';
6
+ import React, { useEffect, useState } from 'react';
7
7
  import './BookingStep.css';
8
8
 
9
9
  export interface BookingStepProps {
10
10
  steps: StepperTimelineItem[];
11
+ currentStep?: number;
11
12
  className?: string;
12
13
  nextDisabled?: boolean;
13
14
  onStepChange?: (step: StepperTimelineItem, index: number) => void;
@@ -15,7 +16,8 @@ export interface BookingStepProps {
15
16
  }
16
17
 
17
18
  export const BookingStep: React.FC<BookingStepProps> = (props) => {
18
- const { steps, className, nextDisabled, onStepChange, onNextClick } = props;
19
+ const { steps, className, nextDisabled, currentStep, onStepChange, onNextClick } =
20
+ props;
19
21
 
20
22
  const [currentIndex, setCurrentIndex] = useState<number>(0);
21
23
 
@@ -30,6 +32,10 @@ export const BookingStep: React.FC<BookingStepProps> = (props) => {
30
32
  onNextClick?.(steps[currentStep], currentStep);
31
33
  };
32
34
 
35
+ useEffect(() => {
36
+ setCurrentIndex(currentStep ? currentStep - 1 : 0);
37
+ }, [currentStep]);
38
+
33
39
  return (
34
40
  <div className="booking-step">
35
41
  <StepperTimeline