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.
- package/dist/components/atoms/Icon/icons/BusIcon.js +1 -1
- package/dist/components/atoms/Icon/icons/CatamaranIcon.js +1 -1
- package/dist/components/atoms/Icon/icons/ClockIcon.d.ts +4 -0
- package/dist/components/atoms/Icon/icons/ClockIcon.js +36 -0
- package/dist/components/atoms/Icon/icons/CloseCircleIcon.js +1 -1
- package/dist/components/atoms/Icon/icons/MapIcon.js +1 -1
- package/dist/components/atoms/Icon/icons/PlusCircleIcon.js +1 -1
- package/dist/components/atoms/Icon/icons/SeaIcon.js +1 -1
- package/dist/components/atoms/Icon/icons/registry.d.ts +1 -0
- package/dist/components/atoms/Icon/icons/registry.js +2 -0
- package/dist/components/atoms/Inputs/Input/Input.d.ts +2 -1
- package/dist/components/atoms/Inputs/Input/Input.js +1 -1
- package/dist/components/atoms/Inputs/Textarea/Textarea.d.ts +3 -1
- package/dist/components/atoms/Inputs/Textarea/Textarea.js +7 -5
- package/dist/components/molecules/BookingPax/BookingPax.d.ts +7 -0
- package/dist/components/molecules/BookingPax/BookingPax.js +21 -0
- package/dist/components/molecules/BookingPax/BookingPaxAccom.d.ts +22 -0
- package/dist/components/molecules/BookingPax/BookingPaxAccom.js +61 -0
- package/dist/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.css +2090 -0
- package/dist/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.d.ts +31 -0
- package/dist/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.js +96 -0
- package/dist/components/molecules/BookingPax/BookingPaxExcursion.d.ts +14 -0
- package/dist/components/molecules/BookingPax/BookingPaxExcursion.js +31 -0
- package/dist/components/molecules/BookingPax/BookingPaxHeader.d.ts +16 -0
- package/dist/components/molecules/BookingPax/BookingPaxHeader.js +28 -0
- package/dist/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.css +2103 -0
- package/dist/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.d.ts +11 -0
- package/dist/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.js +19 -0
- package/dist/components/molecules/BookingPax/BookingPaxRemarks.d.ts +5 -0
- package/dist/components/molecules/BookingPax/BookingPaxRemarks.js +37 -0
- package/dist/components/molecules/BookingPax/BookingPaxTransfer.d.ts +18 -0
- package/dist/components/molecules/BookingPax/BookingPaxTransfer.js +40 -0
- package/dist/components/molecules/BookingPax/index.d.ts +5 -0
- package/dist/components/molecules/BookingPax/index.js +1 -0
- package/dist/components/molecules/Calendar/CalendarInput.d.ts +6 -3
- package/dist/components/molecules/Calendar/CalendarInput.js +10 -10
- package/dist/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.css +2142 -0
- package/dist/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.d.ts +11 -0
- package/dist/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.js +19 -0
- package/dist/components/molecules/DialogContentPolicy/DialogCancellationAccom.d.ts +9 -0
- package/dist/components/molecules/DialogContentPolicy/DialogCancellationAccom.js +24 -0
- package/dist/components/molecules/DialogContentPolicy/DialogCancellationExcursion.d.ts +17 -0
- package/dist/components/molecules/DialogContentPolicy/DialogCancellationExcursion.js +20 -0
- package/dist/components/molecules/DialogContentPolicy/DialogCancellationList.d.ts +11 -0
- package/dist/components/molecules/DialogContentPolicy/DialogCancellationList.js +33 -0
- package/dist/components/molecules/DialogContentPolicy/DialogContentPolicy.d.ts +11 -0
- package/dist/components/molecules/DialogContentPolicy/DialogContentPolicy.js +27 -0
- package/dist/components/molecules/DialogContentPolicy/index.d.ts +4 -0
- package/dist/components/molecules/DialogContentPolicy/index.js +1 -0
- package/dist/components/molecules/ServiceTitle/ServiceTitle.css +3 -0
- package/dist/components/molecules/ServiceTitle/ServiceTitle.d.ts +7 -1
- package/dist/components/molecules/ServiceTitle/ServiceTitle.js +4 -3
- package/dist/components/organisms/Booking/Booking.d.ts +2 -0
- package/dist/components/organisms/Booking/Booking.js +4 -0
- package/dist/components/organisms/Booking/BookingHeader.d.ts +8 -0
- package/dist/components/organisms/Booking/BookingHeader.js +17 -0
- package/dist/components/organisms/Booking/BookingPaxList.d.ts +25 -0
- package/dist/components/organisms/Booking/BookingPaxList.js +117 -0
- package/dist/components/organisms/Booking/BookingStep/BookingStep.d.ts +1 -0
- package/dist/components/organisms/Booking/BookingStep/BookingStep.js +5 -2
- package/dist/components/organisms/DateTimePicker/DateTimePicker.d.ts +6 -3
- package/dist/components/organisms/DateTimePicker/DateTimePicker.js +28 -22
- package/dist/components/organisms/DialogBookingConfirm/DialogBookingConfirm.d.ts +10 -0
- package/dist/components/organisms/DialogBookingConfirm/DialogBookingConfirm.js +17 -0
- package/dist/components/ui/checkbox.d.ts +4 -0
- package/dist/components/ui/checkbox.js +31 -0
- package/dist/hooks/useBookingPax.d.ts +8 -0
- package/dist/hooks/useBookingPax.js +43 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +2 -1
- package/src/components/atoms/Icon/icons/BusIcon.tsx +1 -1
- package/src/components/atoms/Icon/icons/CatamaranIcon.tsx +1 -1
- package/src/components/atoms/Icon/icons/ClockIcon.tsx +46 -0
- package/src/components/atoms/Icon/icons/CloseCircleIcon.tsx +1 -1
- package/src/components/atoms/Icon/icons/MapIcon.tsx +6 -2
- package/src/components/atoms/Icon/icons/PlusCircleIcon.tsx +1 -1
- package/src/components/atoms/Icon/icons/SeaIcon.tsx +1 -1
- package/src/components/atoms/Icon/icons/registry.tsx +2 -0
- package/src/components/atoms/Inputs/Input/Input.tsx +6 -5
- package/src/components/atoms/Inputs/Textarea/Textarea.tsx +18 -4
- package/src/components/molecules/BookingPax/BookingPax.tsx +12 -0
- package/src/components/molecules/BookingPax/BookingPaxAccom.tsx +120 -0
- package/src/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.css +4 -0
- package/src/components/molecules/BookingPax/BookingPaxClient/BookingPaxClient.tsx +188 -0
- package/src/components/molecules/BookingPax/BookingPaxExcursion.tsx +77 -0
- package/src/components/molecules/BookingPax/BookingPaxHeader.tsx +47 -0
- package/src/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.css +15 -0
- package/src/components/molecules/BookingPax/BookingPaxLayout/BookingPaxLayout.tsx +25 -0
- package/src/components/molecules/BookingPax/BookingPaxRemarks.tsx +46 -0
- package/src/components/molecules/BookingPax/BookingPaxTransfer.tsx +121 -0
- package/src/components/molecules/BookingPax/index.ts +9 -0
- package/src/components/molecules/Calendar/CalendarInput.tsx +26 -24
- package/src/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.css +37 -0
- package/src/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.tsx +28 -0
- package/src/components/molecules/DialogContentPolicy/DialogCancellationAccom.tsx +65 -0
- package/src/components/molecules/DialogContentPolicy/DialogCancellationExcursion.tsx +59 -0
- package/src/components/molecules/DialogContentPolicy/DialogCancellationList.tsx +49 -0
- package/src/components/molecules/DialogContentPolicy/DialogContentPolicy.tsx +45 -0
- package/src/components/molecules/DialogContentPolicy/index.ts +5 -0
- package/src/components/molecules/ServiceTitle/ServiceTitle.css +1 -1
- package/src/components/molecules/ServiceTitle/ServiceTitle.tsx +25 -7
- package/src/components/organisms/Booking/Booking.tsx +4 -0
- package/src/components/organisms/Booking/BookingHeader.tsx +24 -0
- package/src/components/organisms/Booking/BookingPaxList.tsx +224 -0
- package/src/components/organisms/Booking/BookingStep/BookingStep.tsx +8 -2
- package/src/components/organisms/DateTimePicker/DateTimePicker.tsx +69 -49
- package/src/components/organisms/DialogBookingConfirm/DialogBookingConfirm.tsx +25 -0
- package/src/components/ui/checkbox.tsx +32 -0
package/src/components/molecules/DialogContentPolicy/CancellationLayout/CancellationLayout.tsx
ADDED
|
@@ -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,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:
|
|
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 {
|
|
17
|
+
const {
|
|
18
|
+
title,
|
|
19
|
+
textSize = 'md',
|
|
20
|
+
icon,
|
|
21
|
+
iconSize = 'sm',
|
|
22
|
+
textLeading = '5',
|
|
23
|
+
textVariant = 'bold',
|
|
24
|
+
} = props;
|
|
12
25
|
return (
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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 } =
|
|
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
|