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.
Files changed (161) hide show
  1. package/README.md +10 -6
  2. package/dist/components/atoms/Checkbox/Checkbox.js +7 -1
  3. package/dist/components/atoms/Icon/Icon.d.ts +1 -1
  4. package/dist/components/atoms/Icon/Icon.js +22 -1
  5. package/dist/components/atoms/Icon/icons/BuildingIcon.d.ts +8 -0
  6. package/dist/components/atoms/Icon/icons/BuildingIcon.js +36 -0
  7. package/dist/components/atoms/Icon/icons/CalendarOutlineIcon.d.ts +8 -0
  8. package/dist/components/atoms/Icon/icons/CalendarOutlineIcon.js +36 -0
  9. package/dist/components/atoms/Icon/icons/HomeIcon.d.ts +8 -0
  10. package/dist/components/atoms/Icon/icons/HomeIcon.js +25 -0
  11. package/dist/components/atoms/Icon/icons/MinusIcon.d.ts +8 -0
  12. package/dist/components/atoms/Icon/icons/MinusIcon.js +25 -0
  13. package/dist/components/atoms/Icon/icons/PlaneIcon.d.ts +8 -0
  14. package/dist/components/atoms/Icon/icons/PlaneIcon.js +36 -0
  15. package/dist/components/atoms/Icon/icons/PlusIcon.d.ts +8 -0
  16. package/dist/components/atoms/Icon/icons/PlusIcon.js +25 -0
  17. package/dist/components/atoms/Icon/icons/ShipIcon.d.ts +8 -0
  18. package/dist/components/atoms/Icon/icons/ShipIcon.js +36 -0
  19. package/dist/components/atoms/Illustration/Illustration.d.ts +14 -0
  20. package/dist/components/atoms/Illustration/Illustration.js +33 -0
  21. package/dist/components/atoms/Illustration/illustrations.d.ts +51 -0
  22. package/dist/components/atoms/Illustration/illustrations.js +97 -0
  23. package/dist/components/atoms/RatingStar/RatingStar.d.ts +40 -0
  24. package/dist/components/atoms/RatingStar/RatingStar.js +54 -0
  25. package/dist/components/atoms/SegmentedButton/SegmentedButton.d.ts +27 -0
  26. package/dist/components/atoms/SegmentedButton/SegmentedButton.js +49 -0
  27. package/dist/components/atoms/Slider/Slider.d.ts +52 -0
  28. package/dist/components/atoms/Slider/Slider.js +30 -0
  29. package/dist/components/molecules/Calendar/CalendarInput.d.ts +34 -0
  30. package/dist/components/molecules/Calendar/CalendarInput.js +49 -0
  31. package/dist/components/molecules/Calendar/DateTime.d.ts +25 -0
  32. package/dist/components/molecules/Calendar/DateTime.js +106 -0
  33. package/dist/components/molecules/Calendar/TimePicker.d.ts +16 -0
  34. package/dist/components/molecules/Calendar/TimePicker.js +91 -0
  35. package/dist/components/molecules/LocationDropdown/LocationDropdown.d.ts +34 -0
  36. package/dist/components/molecules/LocationDropdown/LocationDropdown.js +120 -0
  37. package/dist/components/molecules/LocationDropdown/index.d.ts +2 -0
  38. package/dist/components/molecules/LocationDropdown/index.js +1 -0
  39. package/dist/components/molecules/RatingTab/RatingTab.d.ts +39 -0
  40. package/dist/components/molecules/RatingTab/RatingTab.js +41 -0
  41. package/dist/components/molecules/TabGroup/TabGroup.d.ts +17 -0
  42. package/dist/components/molecules/TabGroup/TabGroup.js +30 -0
  43. package/dist/components/organisms/CardContainer/CardContainer.d.ts +37 -0
  44. package/dist/components/organisms/CardContainer/CardContainer.js +27 -0
  45. package/dist/components/organisms/DateTimePicker/DateTimePicker.d.ts +15 -0
  46. package/dist/components/organisms/DateTimePicker/DateTimePicker.js +66 -0
  47. package/dist/components/organisms/Dialog/Dialog.d.ts +103 -0
  48. package/dist/components/organisms/Dialog/Dialog.js +162 -0
  49. package/dist/components/organisms/PaxSelector/PaxSelector.d.ts +63 -0
  50. package/dist/components/organisms/PaxSelector/PaxSelector.js +402 -0
  51. package/dist/components/organisms/RoundTrip/RoundTrip.d.ts +54 -0
  52. package/dist/components/organisms/RoundTrip/RoundTrip.js +179 -0
  53. package/dist/components/organisms/RoundTrip/index.d.ts +2 -0
  54. package/dist/components/organisms/RoundTrip/index.js +1 -0
  55. package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.d.ts +35 -0
  56. package/dist/components/organisms/SearchBarTransfer/SearchBarTransfer.js +192 -0
  57. package/dist/components/organisms/SearchBarTransfer/index.d.ts +2 -0
  58. package/dist/components/organisms/SearchBarTransfer/index.js +1 -0
  59. package/dist/components/organisms/TransferLine/TransferLine.d.ts +53 -0
  60. package/dist/components/organisms/TransferLine/TransferLine.js +179 -0
  61. package/dist/components/ui/button.d.ts +10 -0
  62. package/dist/components/ui/button.js +56 -0
  63. package/dist/components/ui/calendar.d.ts +8 -0
  64. package/dist/components/ui/calendar.js +87 -0
  65. package/dist/components/ui/popover.d.ts +7 -0
  66. package/dist/components/ui/popover.js +42 -0
  67. package/dist/index.d.ts +26 -0
  68. package/dist/index.js +18 -0
  69. package/dist/lib/utils.d.ts +7 -0
  70. package/dist/lib/utils.js +13 -0
  71. package/dist/styles/components/avatar.css +2165 -0
  72. package/dist/styles/components/calendar.css +2214 -0
  73. package/dist/styles/components/checkbox.css +2212 -0
  74. package/dist/styles/components/dropdown.css +2295 -0
  75. package/dist/styles/components/forms.css +2229 -0
  76. package/dist/styles/components/illustration.css +2081 -0
  77. package/dist/styles/components/molecule/calendarInput.css +2308 -0
  78. package/dist/styles/components/molecule/dateTime.css +2092 -0
  79. package/dist/styles/components/molecule/location-dropdown.css +2405 -0
  80. package/dist/styles/components/molecule/timePicker.css +2204 -0
  81. package/dist/styles/components/multiselect-dropdown.css +2312 -0
  82. package/dist/styles/components/organism/card-container.css +2128 -0
  83. package/dist/styles/components/organism/dialog.css +2441 -0
  84. package/dist/styles/components/organism/footer.css +2387 -0
  85. package/dist/styles/components/organism/pax-selector.css +2800 -0
  86. package/dist/styles/components/organism/round-trip.css +2143 -0
  87. package/dist/styles/components/organism/search-bar-transfer.css +2258 -0
  88. package/dist/styles/components/organism/topnavigation.css +2499 -0
  89. package/dist/styles/components/organism/transfer-line.css +2208 -0
  90. package/dist/styles/components/rating-star.css +2113 -0
  91. package/dist/styles/components/rating-tab.css +2157 -0
  92. package/dist/styles/components/scrollbar.css +2143 -0
  93. package/dist/styles/components/segmented-button.css +2218 -0
  94. package/dist/styles/components/selected-value.css +2159 -0
  95. package/dist/styles/components/slider.css +2164 -0
  96. package/dist/styles/components/typography.css +2417 -0
  97. package/dist/styles/tokens/_tokens.scss +2072 -0
  98. package/dist/styles/tokens/tokens.css +2075 -0
  99. package/package.json +24 -12
  100. package/src/components/atoms/Button/Button.css +34 -34
  101. package/src/components/atoms/Checkbox/Checkbox.tsx +83 -69
  102. package/src/components/atoms/Icon/Icon.tsx +30 -2
  103. package/src/components/atoms/Icon/icons/BuildingIcon.tsx +50 -0
  104. package/src/components/atoms/Icon/icons/CalendarOutlineIcon.tsx +50 -0
  105. package/src/components/atoms/Icon/icons/HomeIcon.tsx +52 -0
  106. package/src/components/atoms/Icon/icons/MinusIcon.tsx +45 -0
  107. package/src/components/atoms/Icon/icons/PlaneIcon.tsx +50 -0
  108. package/src/components/atoms/Icon/icons/PlusIcon.tsx +45 -0
  109. package/src/components/atoms/Icon/icons/ShipIcon.tsx +50 -0
  110. package/src/components/atoms/Illustration/Illustration.tsx +28 -0
  111. package/src/components/atoms/Illustration/illustrations.ts +116 -0
  112. package/src/components/atoms/RatingStar/RatingStar.tsx +114 -0
  113. package/src/components/atoms/SegmentedButton/SegmentedButton.tsx +94 -0
  114. package/src/components/atoms/Slider/Slider.tsx +95 -0
  115. package/src/components/molecules/Calendar/CalendarInput.tsx +135 -0
  116. package/src/components/molecules/Calendar/DateTime.tsx +172 -0
  117. package/src/components/molecules/Calendar/TimePicker.tsx +174 -0
  118. package/src/components/molecules/LocationDropdown/LocationDropdown.tsx +234 -0
  119. package/src/components/molecules/LocationDropdown/index.ts +2 -0
  120. package/src/components/molecules/RatingTab/RatingTab.tsx +96 -0
  121. package/src/components/molecules/TabGroup/TabGroup.tsx +60 -0
  122. package/src/components/molecules/UserCard/UserCard.stories.tsx +2 -2
  123. package/src/components/organisms/CardContainer/CardContainer.tsx +66 -0
  124. package/src/components/organisms/DateTimePicker/DateTimePicker.tsx +110 -0
  125. package/src/components/organisms/Dialog/Dialog.tsx +352 -0
  126. package/src/components/organisms/PaxSelector/PaxSelector.tsx +979 -0
  127. package/src/components/organisms/RoundTrip/RoundTrip.tsx +335 -0
  128. package/src/components/organisms/RoundTrip/index.ts +2 -0
  129. package/src/components/organisms/SearchBarTransfer/SearchBarTransfer.tsx +388 -0
  130. package/src/components/organisms/SearchBarTransfer/index.ts +2 -0
  131. package/src/components/organisms/TransferLine/TransferLine.tsx +369 -0
  132. package/src/components/ui/button.tsx +60 -0
  133. package/src/components/ui/calendar.tsx +246 -0
  134. package/src/components/ui/popover.tsx +46 -0
  135. package/src/styles/components/calendar.css +86 -0
  136. package/src/styles/components/checkbox.css +130 -132
  137. package/src/styles/components/dropdown.css +40 -40
  138. package/src/styles/components/forms.css +51 -51
  139. package/src/styles/components/illustration.css +7 -0
  140. package/src/styles/components/molecule/calendarInput.css +157 -0
  141. package/src/styles/components/molecule/dateTime.css +14 -0
  142. package/src/styles/components/molecule/location-dropdown.css +204 -0
  143. package/src/styles/components/molecule/timePicker.css +79 -0
  144. package/src/styles/components/multiselect-dropdown.css +230 -231
  145. package/src/styles/components/organism/card-container.css +49 -0
  146. package/src/styles/components/organism/dialog.css +241 -0
  147. package/src/styles/components/organism/pax-selector.css +702 -0
  148. package/src/styles/components/organism/round-trip.css +55 -0
  149. package/src/styles/components/organism/search-bar-transfer.css +128 -0
  150. package/src/styles/components/organism/transfer-line.css +86 -0
  151. package/src/styles/components/rating-star.css +39 -0
  152. package/src/styles/components/rating-tab.css +83 -0
  153. package/src/styles/components/segmented-button.css +134 -0
  154. package/src/styles/components/selected-value.css +16 -16
  155. package/src/styles/components/slider.css +86 -0
  156. package/src/styles/components/typography.css +36 -36
  157. package/src/styles/tokens/tokens.css +1093 -1093
  158. package/dist/components/atoms/Typography/Heading/Heading.d.ts +0 -9
  159. package/dist/components/atoms/Typography/Heading/Heading.js +0 -25
  160. package/dist/components/atoms/Typography/Text/Text.d.ts +0 -10
  161. 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 };