mautourco-components 0.1.2 → 0.2.1
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/README.md +10 -6
- package/dist/components/atoms/Checkbox/Checkbox.js +7 -1
- package/dist/components/atoms/Icon/Icon.d.ts +1 -1
- package/dist/components/atoms/Icon/Icon.js +22 -1
- package/dist/components/atoms/Icon/icons/BuildingIcon.d.ts +8 -0
- package/dist/components/atoms/Icon/icons/BuildingIcon.js +36 -0
- package/dist/components/atoms/Icon/icons/CalendarOutlineIcon.d.ts +8 -0
- package/dist/components/atoms/Icon/icons/CalendarOutlineIcon.js +36 -0
- package/dist/components/atoms/Icon/icons/HomeIcon.d.ts +8 -0
- package/dist/components/atoms/Icon/icons/HomeIcon.js +25 -0
- package/dist/components/atoms/Icon/icons/MinusIcon.d.ts +8 -0
- package/dist/components/atoms/Icon/icons/MinusIcon.js +25 -0
- package/dist/components/atoms/Icon/icons/PlaneIcon.d.ts +8 -0
- package/dist/components/atoms/Icon/icons/PlaneIcon.js +36 -0
- package/dist/components/atoms/Icon/icons/PlusIcon.d.ts +8 -0
- package/dist/components/atoms/Icon/icons/PlusIcon.js +25 -0
- package/dist/components/atoms/Icon/icons/ShipIcon.d.ts +8 -0
- package/dist/components/atoms/Icon/icons/ShipIcon.js +36 -0
- package/dist/components/atoms/Illustration/Illustration.d.ts +14 -0
- package/dist/components/atoms/Illustration/Illustration.js +33 -0
- package/dist/components/atoms/Illustration/illustrations.d.ts +51 -0
- package/dist/components/atoms/Illustration/illustrations.js +97 -0
- package/dist/components/atoms/RatingStar/RatingStar.d.ts +40 -0
- package/dist/components/atoms/RatingStar/RatingStar.js +54 -0
- package/dist/components/atoms/SegmentedButton/SegmentedButton.d.ts +27 -0
- package/dist/components/atoms/SegmentedButton/SegmentedButton.js +49 -0
- package/dist/components/atoms/Slider/Slider.d.ts +52 -0
- package/dist/components/atoms/Slider/Slider.js +30 -0
- package/dist/components/molecules/Calendar/CalendarInput.d.ts +34 -0
- package/dist/components/molecules/Calendar/CalendarInput.js +49 -0
- package/dist/components/molecules/Calendar/DateTime.d.ts +25 -0
- package/dist/components/molecules/Calendar/DateTime.js +106 -0
- package/dist/components/molecules/Calendar/TimePicker.d.ts +16 -0
- package/dist/components/molecules/Calendar/TimePicker.js +91 -0
- package/dist/components/molecules/LocationDropdown/LocationDropdown.d.ts +34 -0
- package/dist/components/molecules/LocationDropdown/LocationDropdown.js +120 -0
- package/dist/components/molecules/LocationDropdown/index.d.ts +2 -0
- package/dist/components/molecules/LocationDropdown/index.js +1 -0
- package/dist/components/molecules/RatingTab/RatingTab.d.ts +39 -0
- package/dist/components/molecules/RatingTab/RatingTab.js +41 -0
- package/dist/components/molecules/TabGroup/TabGroup.d.ts +17 -0
- package/dist/components/molecules/TabGroup/TabGroup.js +30 -0
- package/dist/components/organisms/CardContainer/CardContainer.d.ts +37 -0
- package/dist/components/organisms/CardContainer/CardContainer.js +27 -0
- package/dist/components/organisms/DateTimePicker/DateTimePicker.d.ts +15 -0
- package/dist/components/organisms/DateTimePicker/DateTimePicker.js +66 -0
- package/dist/components/organisms/Dialog/Dialog.d.ts +103 -0
- package/dist/components/organisms/Dialog/Dialog.js +162 -0
- package/dist/components/organisms/PaxSelector/PaxSelector.d.ts +63 -0
- package/dist/components/organisms/PaxSelector/PaxSelector.js +402 -0
- package/dist/components/organisms/RoundTrip/RoundTrip.d.ts +54 -0
- package/dist/components/organisms/RoundTrip/RoundTrip.js +179 -0
- package/dist/components/organisms/RoundTrip/index.d.ts +2 -0
- package/dist/components/organisms/RoundTrip/index.js +1 -0
- package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.d.ts +35 -0
- package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.js +192 -0
- package/dist/components/organisms/SearchBarTransfer/index.d.ts +2 -0
- package/dist/components/organisms/SearchBarTransfer/index.js +1 -0
- package/dist/components/organisms/TransferLine/TransferLine.d.ts +53 -0
- package/dist/components/organisms/TransferLine/TransferLine.js +179 -0
- package/dist/components/ui/button.d.ts +10 -0
- package/dist/components/ui/button.js +56 -0
- package/dist/components/ui/calendar.d.ts +8 -0
- package/dist/components/ui/calendar.js +87 -0
- package/dist/components/ui/popover.d.ts +7 -0
- package/dist/components/ui/popover.js +42 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +18 -0
- package/dist/lib/utils.d.ts +7 -0
- package/dist/lib/utils.js +13 -0
- package/dist/styles/components/avatar.css +2165 -0
- package/dist/styles/components/calendar.css +2214 -0
- package/dist/styles/components/checkbox.css +2212 -0
- package/dist/styles/components/dropdown.css +2295 -0
- package/dist/styles/components/forms.css +2229 -0
- package/dist/styles/components/illustration.css +2081 -0
- package/dist/styles/components/molecule/calendarInput.css +2308 -0
- package/dist/styles/components/molecule/dateTime.css +2092 -0
- package/dist/styles/components/molecule/location-dropdown.css +2405 -0
- package/dist/styles/components/molecule/timePicker.css +2204 -0
- package/dist/styles/components/multiselect-dropdown.css +2312 -0
- package/dist/styles/components/organism/card-container.css +2128 -0
- package/dist/styles/components/organism/dialog.css +2441 -0
- package/dist/styles/components/organism/footer.css +2387 -0
- package/dist/styles/components/organism/pax-selector.css +2800 -0
- package/dist/styles/components/organism/round-trip.css +2143 -0
- package/dist/styles/components/organism/search-bar-transfer.css +2258 -0
- package/dist/styles/components/organism/topnavigation.css +2499 -0
- package/dist/styles/components/organism/transfer-line.css +2208 -0
- package/dist/styles/components/rating-star.css +2113 -0
- package/dist/styles/components/rating-tab.css +2157 -0
- package/dist/styles/components/scrollbar.css +2143 -0
- package/dist/styles/components/segmented-button.css +2218 -0
- package/dist/styles/components/selected-value.css +2159 -0
- package/dist/styles/components/slider.css +2164 -0
- package/dist/styles/components/typography.css +2417 -0
- package/dist/styles/tokens/_tokens.scss +2072 -0
- package/dist/styles/tokens/tokens.css +2075 -0
- package/package.json +24 -12
- package/src/components/atoms/Button/Button.css +34 -34
- package/src/components/atoms/Checkbox/Checkbox.tsx +83 -69
- package/src/components/atoms/Icon/Icon.tsx +30 -2
- package/src/components/atoms/Icon/icons/BuildingIcon.tsx +50 -0
- package/src/components/atoms/Icon/icons/CalendarOutlineIcon.tsx +50 -0
- package/src/components/atoms/Icon/icons/HomeIcon.tsx +52 -0
- package/src/components/atoms/Icon/icons/MinusIcon.tsx +45 -0
- package/src/components/atoms/Icon/icons/PlaneIcon.tsx +50 -0
- package/src/components/atoms/Icon/icons/PlusIcon.tsx +45 -0
- package/src/components/atoms/Icon/icons/ShipIcon.tsx +50 -0
- package/src/components/atoms/Illustration/Illustration.tsx +28 -0
- package/src/components/atoms/Illustration/illustrations.ts +116 -0
- package/src/components/atoms/RatingStar/RatingStar.tsx +114 -0
- package/src/components/atoms/SegmentedButton/SegmentedButton.tsx +94 -0
- package/src/components/atoms/Slider/Slider.tsx +95 -0
- package/src/components/molecules/Calendar/CalendarInput.tsx +135 -0
- package/src/components/molecules/Calendar/DateTime.tsx +172 -0
- package/src/components/molecules/Calendar/TimePicker.tsx +174 -0
- package/src/components/molecules/LocationDropdown/LocationDropdown.tsx +234 -0
- package/src/components/molecules/LocationDropdown/index.ts +2 -0
- package/src/components/molecules/RatingTab/RatingTab.tsx +96 -0
- package/src/components/molecules/TabGroup/TabGroup.tsx +60 -0
- package/src/components/molecules/UserCard/UserCard.stories.tsx +2 -2
- package/src/components/organisms/CardContainer/CardContainer.tsx +66 -0
- package/src/components/organisms/DateTimePicker/DateTimePicker.tsx +110 -0
- package/src/components/organisms/Dialog/Dialog.tsx +352 -0
- package/src/components/organisms/PaxSelector/PaxSelector.tsx +979 -0
- package/src/components/organisms/RoundTrip/RoundTrip.tsx +335 -0
- package/src/components/organisms/RoundTrip/index.ts +2 -0
- package/src/components/organisms/SearchBarTransfer/SearchBarTransfer.tsx +388 -0
- package/src/components/organisms/SearchBarTransfer/index.ts +2 -0
- package/src/components/organisms/TransferLine/TransferLine.tsx +369 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/calendar.tsx +246 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/styles/components/calendar.css +86 -0
- package/src/styles/components/checkbox.css +130 -132
- package/src/styles/components/dropdown.css +40 -40
- package/src/styles/components/forms.css +51 -51
- package/src/styles/components/illustration.css +7 -0
- package/src/styles/components/molecule/calendarInput.css +157 -0
- package/src/styles/components/molecule/dateTime.css +14 -0
- package/src/styles/components/molecule/location-dropdown.css +204 -0
- package/src/styles/components/molecule/timePicker.css +79 -0
- package/src/styles/components/multiselect-dropdown.css +230 -231
- package/src/styles/components/organism/card-container.css +49 -0
- package/src/styles/components/organism/dialog.css +241 -0
- package/src/styles/components/organism/pax-selector.css +702 -0
- package/src/styles/components/organism/round-trip.css +55 -0
- package/src/styles/components/organism/search-bar-transfer.css +128 -0
- package/src/styles/components/organism/transfer-line.css +86 -0
- package/src/styles/components/rating-star.css +39 -0
- package/src/styles/components/rating-tab.css +83 -0
- package/src/styles/components/segmented-button.css +134 -0
- package/src/styles/components/selected-value.css +16 -16
- package/src/styles/components/slider.css +86 -0
- package/src/styles/components/typography.css +36 -36
- package/src/styles/tokens/tokens.css +1093 -1093
- package/dist/components/atoms/Typography/Heading/Heading.d.ts +0 -9
- package/dist/components/atoms/Typography/Heading/Heading.js +0 -25
- package/dist/components/atoms/Typography/Text/Text.d.ts +0 -10
- package/dist/components/atoms/Typography/Text/Text.js +0 -77
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import React, { useState, useCallback, useRef, useEffect } from "react";
|
|
2
|
+
import Icon from "../../atoms/Icon/Icon";
|
|
3
|
+
import { Text } from "../../atoms/Typography/Typography";
|
|
4
|
+
import PaxSelector, { PaxData } from "../PaxSelector/PaxSelector";
|
|
5
|
+
import DateTimePicker from "../DateTimePicker/DateTimePicker";
|
|
6
|
+
import LocationDropdown, {
|
|
7
|
+
LocationOption,
|
|
8
|
+
LocationGroup,
|
|
9
|
+
LocationData,
|
|
10
|
+
} from "../../molecules/LocationDropdown/LocationDropdown";
|
|
11
|
+
import "../../../styles/components/organism/transfer-line.css";
|
|
12
|
+
|
|
13
|
+
export type TransferType = "arrival" | "departure" | "inter-hotel";
|
|
14
|
+
|
|
15
|
+
// Re-export LocationData for convenience
|
|
16
|
+
export type { LocationData };
|
|
17
|
+
|
|
18
|
+
export interface TransferLineData {
|
|
19
|
+
id: string;
|
|
20
|
+
type: TransferType;
|
|
21
|
+
paxData?: PaxData;
|
|
22
|
+
transferDate?: string;
|
|
23
|
+
pickupPoint?: LocationData;
|
|
24
|
+
dropoffPoint?: LocationData;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TransferLineProps {
|
|
28
|
+
/** Unique identifier for this transfer line */
|
|
29
|
+
id: string;
|
|
30
|
+
/** Type of transfer: arrival, departure, or inter-hotel */
|
|
31
|
+
type: TransferType;
|
|
32
|
+
/** Pax data */
|
|
33
|
+
paxData?: PaxData;
|
|
34
|
+
/** Transfer date */
|
|
35
|
+
transferDate?: string;
|
|
36
|
+
/** Selected pickup point ID (for initial value) */
|
|
37
|
+
pickupPoint?: string | number;
|
|
38
|
+
/** Selected dropoff point ID (for initial value) */
|
|
39
|
+
dropoffPoint?: string | number;
|
|
40
|
+
/** All location options - will be filtered based on transfer type */
|
|
41
|
+
locations?: {
|
|
42
|
+
options?: LocationOption[];
|
|
43
|
+
groups?: LocationGroup[];
|
|
44
|
+
};
|
|
45
|
+
/** Callback when pax data changes */
|
|
46
|
+
onPaxChange?: (paxData: PaxData) => void;
|
|
47
|
+
/** Callback when transfer date changes */
|
|
48
|
+
onDateChange?: (date: string) => void;
|
|
49
|
+
/** Callback when pickup point changes */
|
|
50
|
+
onPickupChange?: (location: LocationData | null) => void;
|
|
51
|
+
/** Callback when dropoff point changes */
|
|
52
|
+
onDropoffChange?: (location: LocationData | null) => void;
|
|
53
|
+
/** Callback when any data changes - returns complete TransferLineData */
|
|
54
|
+
onDataChange?: (data: TransferLineData) => void;
|
|
55
|
+
/** Callback when delete button is clicked */
|
|
56
|
+
onDelete?: () => void;
|
|
57
|
+
/** Whether to show the delete button */
|
|
58
|
+
showDelete?: boolean;
|
|
59
|
+
/** Whether to show the title (Arrival, Departure, Inter-Hotel) */
|
|
60
|
+
showTitle?: boolean;
|
|
61
|
+
/** Additional CSS classes */
|
|
62
|
+
className?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const TransferLine: React.FC<TransferLineProps> = ({
|
|
66
|
+
id,
|
|
67
|
+
type,
|
|
68
|
+
paxData,
|
|
69
|
+
transferDate,
|
|
70
|
+
pickupPoint,
|
|
71
|
+
dropoffPoint,
|
|
72
|
+
locations = { options: [], groups: [] },
|
|
73
|
+
onPaxChange,
|
|
74
|
+
onDateChange,
|
|
75
|
+
onPickupChange,
|
|
76
|
+
onDropoffChange,
|
|
77
|
+
onDataChange,
|
|
78
|
+
onDelete,
|
|
79
|
+
showDelete = true,
|
|
80
|
+
showTitle = false,
|
|
81
|
+
className = "",
|
|
82
|
+
}) => {
|
|
83
|
+
const [internalPaxData, setInternalPaxData] = useState<PaxData | undefined>(
|
|
84
|
+
paxData
|
|
85
|
+
);
|
|
86
|
+
const [internalTransferDate, setInternalTransferDate] = useState<
|
|
87
|
+
string | undefined
|
|
88
|
+
>(transferDate);
|
|
89
|
+
const [internalPickupPoint, setInternalPickupPoint] = useState<
|
|
90
|
+
LocationData | null
|
|
91
|
+
>(null);
|
|
92
|
+
const [internalDropoffPoint, setInternalDropoffPoint] = useState<
|
|
93
|
+
LocationData | null
|
|
94
|
+
>(null);
|
|
95
|
+
|
|
96
|
+
// Helper function to get all locations (from options and groups)
|
|
97
|
+
const getAllLocations = (
|
|
98
|
+
options: LocationOption[],
|
|
99
|
+
groups: LocationGroup[]
|
|
100
|
+
): LocationOption[] => {
|
|
101
|
+
const groupOptions = groups.flatMap((group) => group.options);
|
|
102
|
+
return [...options, ...groupOptions];
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Filter locations based on transfer type and position (pickup/dropoff)
|
|
106
|
+
const filterLocations = (position: 'pickup' | 'dropoff') => {
|
|
107
|
+
const allOptions = locations.options || [];
|
|
108
|
+
const allGroups = locations.groups || [];
|
|
109
|
+
|
|
110
|
+
let filterTypes: Array<'airport' | 'port' | 'accommodation'> = [];
|
|
111
|
+
|
|
112
|
+
if (type === 'arrival') {
|
|
113
|
+
filterTypes = position === 'pickup' ? ['airport', 'port'] : ['accommodation'];
|
|
114
|
+
} else if (type === 'departure') {
|
|
115
|
+
filterTypes = position === 'pickup' ? ['accommodation'] : ['airport', 'port'];
|
|
116
|
+
} else if (type === 'inter-hotel') {
|
|
117
|
+
filterTypes = ['accommodation'];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const filteredOptions = allOptions.filter(opt => filterTypes.includes(opt.type));
|
|
121
|
+
const filteredGroups = allGroups
|
|
122
|
+
.map(group => ({
|
|
123
|
+
...group,
|
|
124
|
+
options: group.options.filter(opt => filterTypes.includes(opt.type))
|
|
125
|
+
}))
|
|
126
|
+
.filter(group => group.options.length > 0);
|
|
127
|
+
|
|
128
|
+
return { options: filteredOptions, groups: filteredGroups };
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Get default location (first airport for arrival/departure)
|
|
132
|
+
const getDefaultLocation = (position: 'pickup' | 'dropoff'): LocationOption | null => {
|
|
133
|
+
const { options, groups } = filterLocations(position);
|
|
134
|
+
const allLocations = getAllLocations(options, groups);
|
|
135
|
+
|
|
136
|
+
// For arrival pickup or departure dropoff, default to first airport
|
|
137
|
+
if ((type === 'arrival' && position === 'pickup') || (type === 'departure' && position === 'dropoff')) {
|
|
138
|
+
return allLocations.find(loc => loc.type === 'airport') || allLocations[0] || null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return null;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Initialize location data from IDs or set defaults
|
|
145
|
+
React.useEffect(() => {
|
|
146
|
+
const { options, groups } = filterLocations('pickup');
|
|
147
|
+
const allPickupLocations = getAllLocations(options, groups);
|
|
148
|
+
|
|
149
|
+
if (pickupPoint) {
|
|
150
|
+
const location = allPickupLocations.find((loc) => loc.id === pickupPoint);
|
|
151
|
+
if (location) {
|
|
152
|
+
setInternalPickupPoint({
|
|
153
|
+
id: location.id,
|
|
154
|
+
locationName: location.label,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
// Set default location if applicable
|
|
159
|
+
const defaultLocation = getDefaultLocation('pickup');
|
|
160
|
+
if (defaultLocation) {
|
|
161
|
+
const defaultData: LocationData = {
|
|
162
|
+
id: defaultLocation.id,
|
|
163
|
+
locationName: defaultLocation.label,
|
|
164
|
+
};
|
|
165
|
+
setInternalPickupPoint(defaultData);
|
|
166
|
+
onPickupChange?.(defaultData);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
170
|
+
}, [pickupPoint, type, locations]);
|
|
171
|
+
|
|
172
|
+
React.useEffect(() => {
|
|
173
|
+
const { options, groups } = filterLocations('dropoff');
|
|
174
|
+
const allDropoffLocations = getAllLocations(options, groups);
|
|
175
|
+
|
|
176
|
+
if (dropoffPoint) {
|
|
177
|
+
const location = allDropoffLocations.find((loc) => loc.id === dropoffPoint);
|
|
178
|
+
if (location) {
|
|
179
|
+
setInternalDropoffPoint({
|
|
180
|
+
id: location.id,
|
|
181
|
+
locationName: location.label,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
// Set default location if applicable
|
|
186
|
+
const defaultLocation = getDefaultLocation('dropoff');
|
|
187
|
+
if (defaultLocation) {
|
|
188
|
+
const defaultData: LocationData = {
|
|
189
|
+
id: defaultLocation.id,
|
|
190
|
+
locationName: defaultLocation.label,
|
|
191
|
+
};
|
|
192
|
+
setInternalDropoffPoint(defaultData);
|
|
193
|
+
onDropoffChange?.(defaultData);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
197
|
+
}, [dropoffPoint, type, locations]);
|
|
198
|
+
|
|
199
|
+
// Update complete data whenever any field changes
|
|
200
|
+
React.useEffect(() => {
|
|
201
|
+
const completeData: TransferLineData = {
|
|
202
|
+
id,
|
|
203
|
+
type,
|
|
204
|
+
paxData: internalPaxData,
|
|
205
|
+
transferDate: internalTransferDate,
|
|
206
|
+
pickupPoint: internalPickupPoint || undefined,
|
|
207
|
+
dropoffPoint: internalDropoffPoint || undefined,
|
|
208
|
+
};
|
|
209
|
+
onDataChange?.(completeData);
|
|
210
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
211
|
+
}, [id, type, internalPaxData, internalTransferDate, internalPickupPoint, internalDropoffPoint]);
|
|
212
|
+
|
|
213
|
+
const getTypeIcon = () => {
|
|
214
|
+
switch (type) {
|
|
215
|
+
case "arrival":
|
|
216
|
+
return "arrival";
|
|
217
|
+
case "departure":
|
|
218
|
+
return "departure";
|
|
219
|
+
case "inter-hotel":
|
|
220
|
+
return "building";
|
|
221
|
+
default:
|
|
222
|
+
return "arrival";
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const getTypeLabel = () => {
|
|
227
|
+
switch (type) {
|
|
228
|
+
case "arrival":
|
|
229
|
+
return "Arrival";
|
|
230
|
+
case "departure":
|
|
231
|
+
return "Departure";
|
|
232
|
+
case "inter-hotel":
|
|
233
|
+
return "Inter-Hotel";
|
|
234
|
+
default:
|
|
235
|
+
return "Transfer";
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const handlePaxChange = (newPaxData: PaxData) => {
|
|
240
|
+
setInternalPaxData(newPaxData);
|
|
241
|
+
onPaxChange?.(newPaxData);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const handleDateChange = (newDate: string | string[]) => {
|
|
245
|
+
const dateValue = Array.isArray(newDate) ? newDate[0] : newDate;
|
|
246
|
+
setInternalTransferDate(dateValue);
|
|
247
|
+
onDateChange?.(dateValue);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const handlePickupChange = (location: LocationData | null) => {
|
|
251
|
+
setInternalPickupPoint(location);
|
|
252
|
+
onPickupChange?.(location);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const handleDropoffChange = (location: LocationData | null) => {
|
|
256
|
+
setInternalDropoffPoint(location);
|
|
257
|
+
onDropoffChange?.(location);
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<div
|
|
262
|
+
className={`transfer-line transfer-line--${type} ${className}`}
|
|
263
|
+
data-transfer-id={id}
|
|
264
|
+
>
|
|
265
|
+
{/* Transfer type header */}
|
|
266
|
+
{showTitle && (
|
|
267
|
+
<div className="transfer-line__header">
|
|
268
|
+
<Icon
|
|
269
|
+
name={getTypeIcon()}
|
|
270
|
+
size="sm"
|
|
271
|
+
className="transfer-line__header-icon"
|
|
272
|
+
/>
|
|
273
|
+
<Text
|
|
274
|
+
size="sm"
|
|
275
|
+
variant="medium"
|
|
276
|
+
className="transfer-line__header-label"
|
|
277
|
+
>
|
|
278
|
+
{getTypeLabel()}
|
|
279
|
+
</Text>
|
|
280
|
+
</div>
|
|
281
|
+
)}
|
|
282
|
+
|
|
283
|
+
{/* Transfer line content */}
|
|
284
|
+
<div className="transfer-line__content-container">
|
|
285
|
+
<div className="transfer-line__content">
|
|
286
|
+
{/* Number of pax */}
|
|
287
|
+
<div className="transfer-line__field transfer-line__field--pax">
|
|
288
|
+
<PaxSelector
|
|
289
|
+
label="Number of pax"
|
|
290
|
+
value={internalPaxData}
|
|
291
|
+
onChange={handlePaxChange}
|
|
292
|
+
placeholder="2 pax"
|
|
293
|
+
/>
|
|
294
|
+
</div>
|
|
295
|
+
|
|
296
|
+
{/* Transfer date */}
|
|
297
|
+
<div className="transfer-line__field transfer-line__field--date">
|
|
298
|
+
<Text
|
|
299
|
+
size="sm"
|
|
300
|
+
variant="regular"
|
|
301
|
+
className="transfer-line__field-label"
|
|
302
|
+
>
|
|
303
|
+
Transfer date
|
|
304
|
+
</Text>
|
|
305
|
+
<DateTimePicker
|
|
306
|
+
placeholder="DD/MM/YYYY"
|
|
307
|
+
mode="calendar"
|
|
308
|
+
iconPosition="left"
|
|
309
|
+
numberOfMonths={1}
|
|
310
|
+
iconBGFull={false}
|
|
311
|
+
showChevron={true}
|
|
312
|
+
onValueChange={handleDateChange}
|
|
313
|
+
selectionMode="single"
|
|
314
|
+
/>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
{/* Pick-up point */}
|
|
318
|
+
<div className="transfer-line__field transfer-line__field--pickup">
|
|
319
|
+
<LocationDropdown
|
|
320
|
+
label="Pick-up point"
|
|
321
|
+
options={filterLocations('pickup').options}
|
|
322
|
+
groups={filterLocations('pickup').groups}
|
|
323
|
+
selectedValue={internalPickupPoint?.id || null}
|
|
324
|
+
onSelectionChange={handlePickupChange}
|
|
325
|
+
placeholder="Select a pick-up point"
|
|
326
|
+
direction="pickup"
|
|
327
|
+
type={type === 'inter-hotel' ? 'accommodation' : type === 'arrival' ? 'airport-port' : 'accommodation'}
|
|
328
|
+
showGroupTitles={false}
|
|
329
|
+
/>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
{/* Drop-off point */}
|
|
333
|
+
<div className="transfer-line__field transfer-line__field--dropoff">
|
|
334
|
+
<LocationDropdown
|
|
335
|
+
label="Drop-off point"
|
|
336
|
+
options={filterLocations('dropoff').options}
|
|
337
|
+
groups={filterLocations('dropoff').groups}
|
|
338
|
+
selectedValue={internalDropoffPoint?.id || null}
|
|
339
|
+
onSelectionChange={handleDropoffChange}
|
|
340
|
+
placeholder="Select a drop-off point"
|
|
341
|
+
direction="dropoff"
|
|
342
|
+
type={type === 'inter-hotel' ? 'accommodation' : type === 'departure' ? 'airport-port' : 'accommodation'}
|
|
343
|
+
showGroupTitles={false}
|
|
344
|
+
/>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
{/* Delete button */}
|
|
348
|
+
{showDelete && (
|
|
349
|
+
<div className="transfer-line__delete">
|
|
350
|
+
<button
|
|
351
|
+
type="button"
|
|
352
|
+
className="transfer-line__delete-btn"
|
|
353
|
+
onClick={onDelete}
|
|
354
|
+
aria-label="Delete transfer line"
|
|
355
|
+
>
|
|
356
|
+
<Icon
|
|
357
|
+
name="delete"
|
|
358
|
+
size="sm"
|
|
359
|
+
className="transfer-line__delete-icon"
|
|
360
|
+
/>
|
|
361
|
+
</button>
|
|
362
|
+
</div>
|
|
363
|
+
)}
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
);
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
export default TransferLine;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
13
|
+
destructive:
|
|
14
|
+
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
15
|
+
outline:
|
|
16
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
17
|
+
secondary:
|
|
18
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
19
|
+
ghost:
|
|
20
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
25
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
26
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
27
|
+
icon: "size-9",
|
|
28
|
+
"icon-sm": "size-8",
|
|
29
|
+
"icon-lg": "size-10",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
variant: "default",
|
|
34
|
+
size: "default",
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
function Button({
|
|
40
|
+
className,
|
|
41
|
+
variant,
|
|
42
|
+
size,
|
|
43
|
+
asChild = false,
|
|
44
|
+
...props
|
|
45
|
+
}: React.ComponentProps<"button"> &
|
|
46
|
+
VariantProps<typeof buttonVariants> & {
|
|
47
|
+
asChild?: boolean
|
|
48
|
+
}) {
|
|
49
|
+
const Comp = asChild ? Slot : "button"
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Comp
|
|
53
|
+
data-slot="button"
|
|
54
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
ChevronDownIcon,
|
|
6
|
+
ChevronLeftIcon,
|
|
7
|
+
ChevronRightIcon,
|
|
8
|
+
} from "lucide-react";
|
|
9
|
+
import {
|
|
10
|
+
CaptionLabelProps,
|
|
11
|
+
DayButton,
|
|
12
|
+
DayPicker,
|
|
13
|
+
getDefaultClassNames,
|
|
14
|
+
MonthGrid,
|
|
15
|
+
MonthGridProps,
|
|
16
|
+
} from "react-day-picker";
|
|
17
|
+
|
|
18
|
+
import { Button, buttonVariants } from "./button";
|
|
19
|
+
import { cn } from "../../lib/utils";
|
|
20
|
+
|
|
21
|
+
function Calendar({
|
|
22
|
+
className,
|
|
23
|
+
classNames,
|
|
24
|
+
showOutsideDays = true,
|
|
25
|
+
captionLayout = "label",
|
|
26
|
+
buttonVariant = "ghost",
|
|
27
|
+
formatters,
|
|
28
|
+
components,
|
|
29
|
+
...props
|
|
30
|
+
}: React.ComponentProps<typeof DayPicker> & {
|
|
31
|
+
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
|
|
32
|
+
}) {
|
|
33
|
+
const defaultClassNames = getDefaultClassNames();
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<DayPicker
|
|
37
|
+
showOutsideDays={showOutsideDays}
|
|
38
|
+
className={cn(
|
|
39
|
+
"bg-background group/calendar p-3 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
|
40
|
+
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
41
|
+
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
captionLayout={captionLayout}
|
|
45
|
+
formatters={{
|
|
46
|
+
formatMonthDropdown: (date) =>
|
|
47
|
+
date.toLocaleString("default", { month: "short" }),
|
|
48
|
+
...formatters,
|
|
49
|
+
}}
|
|
50
|
+
classNames={{
|
|
51
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
52
|
+
months: cn(
|
|
53
|
+
"relative flex flex-col gap-4 md:flex-row",
|
|
54
|
+
defaultClassNames.months
|
|
55
|
+
),
|
|
56
|
+
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
|
57
|
+
nav: cn(
|
|
58
|
+
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
|
59
|
+
defaultClassNames.nav
|
|
60
|
+
),
|
|
61
|
+
button_previous: cn(
|
|
62
|
+
buttonVariants({ variant: buttonVariant }),
|
|
63
|
+
"h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50",
|
|
64
|
+
defaultClassNames.button_previous
|
|
65
|
+
),
|
|
66
|
+
button_next: cn(
|
|
67
|
+
buttonVariants({ variant: buttonVariant }),
|
|
68
|
+
"h-[--cell-size] w-[--cell-size] select-none p-0 aria-disabled:opacity-50",
|
|
69
|
+
defaultClassNames.button_next
|
|
70
|
+
),
|
|
71
|
+
month_caption: cn(
|
|
72
|
+
"flex h-[--cell-size] w-full items-center justify-center px-[--cell-size]",
|
|
73
|
+
defaultClassNames.month_caption
|
|
74
|
+
),
|
|
75
|
+
dropdowns: cn(
|
|
76
|
+
"flex h-[--cell-size] w-full items-center justify-center gap-1.5 text-sm font-medium",
|
|
77
|
+
defaultClassNames.dropdowns
|
|
78
|
+
),
|
|
79
|
+
dropdown_root: cn(
|
|
80
|
+
"has-focus:border-ring border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] relative rounded-md border",
|
|
81
|
+
defaultClassNames.dropdown_root
|
|
82
|
+
),
|
|
83
|
+
dropdown: cn("absolute inset-0 opacity-0", defaultClassNames.dropdown),
|
|
84
|
+
caption_label: cn(
|
|
85
|
+
"select-none font-medium",
|
|
86
|
+
captionLayout === "label"
|
|
87
|
+
? "text-sm"
|
|
88
|
+
: "[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5",
|
|
89
|
+
defaultClassNames.caption_label,
|
|
90
|
+
"calendar--caption_label"
|
|
91
|
+
),
|
|
92
|
+
table: "w-full border-collapse",
|
|
93
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
94
|
+
weekday: cn(
|
|
95
|
+
"text-muted-foreground flex-1 select-none rounded-md text-[0.8rem] font-normal",
|
|
96
|
+
defaultClassNames.weekday,
|
|
97
|
+
"calendar--weekday"
|
|
98
|
+
),
|
|
99
|
+
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
|
100
|
+
week_number_header: cn(
|
|
101
|
+
"w-[--cell-size] select-none",
|
|
102
|
+
defaultClassNames.week_number_header
|
|
103
|
+
),
|
|
104
|
+
week_number: cn(
|
|
105
|
+
"text-muted-foreground select-none text-[0.8rem]",
|
|
106
|
+
defaultClassNames.week_number
|
|
107
|
+
),
|
|
108
|
+
day: cn(
|
|
109
|
+
"relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
|
|
110
|
+
props.showWeekNumber
|
|
111
|
+
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
|
|
112
|
+
: "[&:first-child[data-selected=true]_button]:rounded-l-md",
|
|
113
|
+
defaultClassNames.day
|
|
114
|
+
),
|
|
115
|
+
range_start: cn(
|
|
116
|
+
"bg-accent rounded-l-md",
|
|
117
|
+
defaultClassNames.range_start
|
|
118
|
+
),
|
|
119
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
120
|
+
range_end: cn("bg-accent rounded-r-md", defaultClassNames.range_end),
|
|
121
|
+
today: cn(
|
|
122
|
+
"bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
|
|
123
|
+
defaultClassNames.today
|
|
124
|
+
),
|
|
125
|
+
outside: cn(
|
|
126
|
+
"text-muted-foreground aria-selected:text-muted-foreground",
|
|
127
|
+
defaultClassNames.outside
|
|
128
|
+
),
|
|
129
|
+
disabled: cn(
|
|
130
|
+
"text-muted-foreground opacity-50",
|
|
131
|
+
defaultClassNames.disabled
|
|
132
|
+
),
|
|
133
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
134
|
+
...classNames,
|
|
135
|
+
}}
|
|
136
|
+
components={{
|
|
137
|
+
Root: ({ className, rootRef, ...props }) => {
|
|
138
|
+
return (
|
|
139
|
+
<div
|
|
140
|
+
data-slot="calendar"
|
|
141
|
+
ref={rootRef}
|
|
142
|
+
className={cn(className)}
|
|
143
|
+
{...props}
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
146
|
+
},
|
|
147
|
+
Chevron: ({ className, orientation, ...props }) => {
|
|
148
|
+
if (orientation === "left") {
|
|
149
|
+
return (
|
|
150
|
+
<ChevronLeftIcon className={cn("size-4", className)} {...props} />
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (orientation === "right") {
|
|
155
|
+
return (
|
|
156
|
+
<ChevronRightIcon
|
|
157
|
+
className={cn("size-4", className)}
|
|
158
|
+
{...props}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<ChevronDownIcon className={cn("size-4", className)} {...props} />
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
DayButton: CalendarDayButton,
|
|
168
|
+
WeekNumber: ({ children, ...props }) => {
|
|
169
|
+
return (
|
|
170
|
+
<td {...props}>
|
|
171
|
+
<div className="flex size-[--cell-size] items-center justify-center text-center">
|
|
172
|
+
{children}
|
|
173
|
+
</div>
|
|
174
|
+
</td>
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
MonthGrid: CustomMonthGrid,
|
|
178
|
+
CaptionLabel: CustomCaptionLabel,
|
|
179
|
+
...components,
|
|
180
|
+
}}
|
|
181
|
+
{...props}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function CalendarDayButton({
|
|
187
|
+
className,
|
|
188
|
+
day,
|
|
189
|
+
modifiers,
|
|
190
|
+
...props
|
|
191
|
+
}: React.ComponentProps<typeof DayButton>) {
|
|
192
|
+
const defaultClassNames = getDefaultClassNames();
|
|
193
|
+
|
|
194
|
+
const ref = React.useRef<HTMLButtonElement>(null);
|
|
195
|
+
React.useEffect(() => {
|
|
196
|
+
if (modifiers.focused) ref.current?.focus();
|
|
197
|
+
}, [modifiers.focused]);
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<Button
|
|
201
|
+
ref={ref}
|
|
202
|
+
variant="ghost"
|
|
203
|
+
size="icon"
|
|
204
|
+
data-day={day.date.toLocaleDateString()}
|
|
205
|
+
data-selected-single={
|
|
206
|
+
modifiers.selected &&
|
|
207
|
+
!modifiers.range_start &&
|
|
208
|
+
!modifiers.range_end &&
|
|
209
|
+
!modifiers.range_middle
|
|
210
|
+
}
|
|
211
|
+
data-range-start={modifiers.range_start}
|
|
212
|
+
data-range-end={modifiers.range_end}
|
|
213
|
+
data-range-middle={modifiers.range_middle}
|
|
214
|
+
className={cn(
|
|
215
|
+
// "data-[selected-single=true]:bg-[#FFAB63] data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-[#FFAB63] data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-[#FFAB63] data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 font-normal leading-none data-[range-end=true]:rounded-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] [&>span]:text-xs [&>span]:opacity-70",
|
|
216
|
+
defaultClassNames.day,
|
|
217
|
+
className
|
|
218
|
+
// 'data-[selected-single=true]:!rounded-full data-[range-start=true]:!rounded-full data-[range-end=true]:!rounded-full data-[range-middle=true]:bg-[#FFEBD4]'
|
|
219
|
+
)}
|
|
220
|
+
{...props}
|
|
221
|
+
>
|
|
222
|
+
<span className="relative z-20">{day.date.getDate()}</span>
|
|
223
|
+
</Button>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function CustomMonthGrid(props: MonthGridProps) {
|
|
228
|
+
return (
|
|
229
|
+
<>
|
|
230
|
+
<hr className="calendar--separator"></hr>
|
|
231
|
+
<MonthGrid {...props} />
|
|
232
|
+
</>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function CustomCaptionLabel(props: CaptionLabelProps) {
|
|
237
|
+
const { children } = props;
|
|
238
|
+
const [month, year] = String(children).split(" ");
|
|
239
|
+
return (
|
|
240
|
+
<span {...props}>
|
|
241
|
+
{month}, {year}
|
|
242
|
+
</span>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export { Calendar, CalendarDayButton };
|