@ticketboothapp/booking 0.1.22 → 1.2.24

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 (158) hide show
  1. package/package.json +2 -29
  2. package/src/index.ts +0 -79
  3. package/tsconfig.json +2 -8
  4. package/src/assets/icons/minus.svg +0 -7
  5. package/src/assets/icons/partner-logos/getyourguide.svg +0 -8
  6. package/src/assets/icons/plus.svg +0 -3
  7. package/src/colours.css +0 -23
  8. package/src/components/BookingDetails.module.css +0 -1591
  9. package/src/components/BookingDetails.tsx +0 -2264
  10. package/src/components/BookingWidget.tsx +0 -302
  11. package/src/components/ManageBookingView.tsx +0 -437
  12. package/src/components/PhoneInputWithCountry.module.css +0 -131
  13. package/src/components/PhoneInputWithCountry.tsx +0 -44
  14. package/src/components/PickupLocationDialog.module.css +0 -360
  15. package/src/components/PickupLocationDialog.tsx +0 -357
  16. package/src/components/PostBookingDependentAddOnUpsell.module.css +0 -174
  17. package/src/components/PostBookingDependentAddOnUpsell.tsx +0 -407
  18. package/src/components/booking/AddOnsSection.module.css +0 -10
  19. package/src/components/booking/AddOnsSection.tsx +0 -184
  20. package/src/components/booking/AdminPaymentChoiceModal.tsx +0 -98
  21. package/src/components/booking/BookingDialog.module.css +0 -643
  22. package/src/components/booking/BookingDialog.tsx +0 -356
  23. package/src/components/booking/BookingFlow.tsx +0 -4385
  24. package/src/components/booking/BookingFlowCollage.module.css +0 -148
  25. package/src/components/booking/BookingFlowCollage.tsx +0 -184
  26. package/src/components/booking/BookingFlowPlaceholder.module.css +0 -27
  27. package/src/components/booking/BookingFlowPlaceholder.tsx +0 -25
  28. package/src/components/booking/BookingFlowPreview.tsx +0 -51
  29. package/src/components/booking/BookingProductGrid.module.css +0 -359
  30. package/src/components/booking/BookingProductGrid.tsx +0 -497
  31. package/src/components/booking/Calendar.module.css +0 -616
  32. package/src/components/booking/Calendar.tsx +0 -1123
  33. package/src/components/booking/CancellationPolicySelector.module.css +0 -124
  34. package/src/components/booking/CancellationPolicySelector.tsx +0 -142
  35. package/src/components/booking/ChangeBookingDialog.tsx +0 -562
  36. package/src/components/booking/CheckoutForm.module.css +0 -244
  37. package/src/components/booking/CheckoutForm.tsx +0 -364
  38. package/src/components/booking/CheckoutModal.tsx +0 -451
  39. package/src/components/booking/CurrencySwitcher.tsx +0 -81
  40. package/src/components/booking/DapFlowCollage.tsx +0 -88
  41. package/src/components/booking/DapTourDescription.tsx +0 -35
  42. package/src/components/booking/DependentAddOnBookingDialog.tsx +0 -1350
  43. package/src/components/booking/DependentAddOnPaymentForm.tsx +0 -124
  44. package/src/components/booking/ErrorBoundary.tsx +0 -63
  45. package/src/components/booking/InfoTooltip.tsx +0 -108
  46. package/src/components/booking/ItineraryBox.module.css +0 -258
  47. package/src/components/booking/ItineraryBox.tsx +0 -550
  48. package/src/components/booking/ItineraryBuilder.tsx +0 -82
  49. package/src/components/booking/ItineraryPlaceholder.module.css +0 -45
  50. package/src/components/booking/ItineraryPlaceholder.tsx +0 -26
  51. package/src/components/booking/MealDrinkAddOnSelector.tsx +0 -338
  52. package/src/components/booking/PickupLocationSelector.module.css +0 -124
  53. package/src/components/booking/PickupLocationSelector.tsx +0 -1566
  54. package/src/components/booking/PickupTimeSelector.module.css +0 -134
  55. package/src/components/booking/PickupTimeSelector.tsx +0 -112
  56. package/src/components/booking/PriceBreakdown.tsx +0 -154
  57. package/src/components/booking/PriceSummary.tsx +0 -234
  58. package/src/components/booking/PrivateShuttleBookingFlow.module.css +0 -357
  59. package/src/components/booking/PrivateShuttleBookingFlow.tsx +0 -2662
  60. package/src/components/booking/PromoCodeInput.module.css +0 -166
  61. package/src/components/booking/PromoCodeInput.tsx +0 -99
  62. package/src/components/booking/ReturnTimeSelector.module.css +0 -173
  63. package/src/components/booking/ReturnTimeSelector.tsx +0 -145
  64. package/src/components/booking/TermsAcceptance.tsx +0 -111
  65. package/src/components/booking/TicketSelector.module.css +0 -164
  66. package/src/components/booking/TicketSelector.tsx +0 -199
  67. package/src/components/booking/TourDescription.module.css +0 -304
  68. package/src/components/booking/TourDescription.tsx +0 -273
  69. package/src/components/booking/booking-flow-ui.ts +0 -38
  70. package/src/components/booking/booking-flow.css +0 -944
  71. package/src/components/button.css +0 -245
  72. package/src/components/button.tsx +0 -152
  73. package/src/components/colorable-svg.tsx +0 -29
  74. package/src/components/image.css +0 -29
  75. package/src/components/image.tsx +0 -113
  76. package/src/components/partner/PartnerBookingPage.module.css +0 -130
  77. package/src/components/partner/PartnerBookingPage.tsx +0 -390
  78. package/src/components/partner/PartnerBookingPageWithBrowserMetadata.tsx +0 -45
  79. package/src/components/product-tag.module.css +0 -30
  80. package/src/components/product-tag.tsx +0 -34
  81. package/src/components/product-theme-pages/image-modal.tsx +0 -248
  82. package/src/components/product-theme-pages/photo-gallery.module.css +0 -200
  83. package/src/components/terms/TermsContent.tsx +0 -178
  84. package/src/components/value-pill.module.css +0 -59
  85. package/src/components/value-pill.tsx +0 -46
  86. package/src/constants/images.ts +0 -556
  87. package/src/constants/pill-values.ts +0 -210
  88. package/src/constants/products.ts +0 -155
  89. package/src/contexts/AvailabilitiesCacheContext.tsx +0 -125
  90. package/src/contexts/BookingAppContext.tsx +0 -134
  91. package/src/contexts/CompanyContext.tsx +0 -70
  92. package/src/data/dap-descriptions/session-couples-families-friends.en.json +0 -61
  93. package/src/data/dap-descriptions/session-elopements.en.json +0 -60
  94. package/src/data/dap-descriptions/session-proposals.en.json +0 -60
  95. package/src/data/product-descriptions/afternoon-delight.en.json +0 -35
  96. package/src/data/product-descriptions/emerald-lake-escape.en.json +0 -68
  97. package/src/data/product-descriptions/lake-louise-adventure.en.json +0 -74
  98. package/src/data/product-descriptions/moraine-lake-adventure.en.json +0 -78
  99. package/src/data/product-descriptions/moraine-lake-sunrise-lake-louise-golden-hour.en.json +0 -65
  100. package/src/data/product-descriptions/moraine-lake-sunrise.en.json +0 -64
  101. package/src/data/product-descriptions/private-tour.en.json +0 -80
  102. package/src/data/product-descriptions/two-lakes-combo.en.json +0 -65
  103. package/src/data/products-config.json +0 -101
  104. package/src/hooks/useBookingSourceMetadataFromLocation.ts +0 -21
  105. package/src/hooks/useIsBookingLaunchLive.ts +0 -49
  106. package/src/lib/analytics.ts +0 -197
  107. package/src/lib/booking/booking-source.ts +0 -51
  108. package/src/lib/booking/checkout-breakdown.ts +0 -69
  109. package/src/lib/booking/correlation-id.ts +0 -46
  110. package/src/lib/booking/i18n/config.ts +0 -21
  111. package/src/lib/booking/i18n/index.tsx +0 -144
  112. package/src/lib/booking/i18n/messages/en.json +0 -236
  113. package/src/lib/booking/i18n/messages/fr.json +0 -236
  114. package/src/lib/booking/itinerary-display.ts +0 -36
  115. package/src/lib/booking/itinerary-labels.ts +0 -70
  116. package/src/lib/booking/location-calculations.ts +0 -43
  117. package/src/lib/booking/location-utils.ts +0 -165
  118. package/src/lib/booking/map-utils.ts +0 -153
  119. package/src/lib/booking/marker-icons.ts +0 -113
  120. package/src/lib/booking/normalize-booking-product-id.ts +0 -21
  121. package/src/lib/booking/pickup-location-types.ts +0 -25
  122. package/src/lib/booking/places-api.ts +0 -154
  123. package/src/lib/booking/pricing.ts +0 -466
  124. package/src/lib/booking/product-option-id.ts +0 -35
  125. package/src/lib/booking/source-metadata.ts +0 -226
  126. package/src/lib/booking/sunday-week.ts +0 -14
  127. package/src/lib/booking/theme.ts +0 -83
  128. package/src/lib/booking/trace-context.ts +0 -62
  129. package/src/lib/booking/utils.ts +0 -9
  130. package/src/lib/booking-api.ts +0 -1793
  131. package/src/lib/booking-constants.ts +0 -23
  132. package/src/lib/booking-ref.ts +0 -13
  133. package/src/lib/booking-types.ts +0 -36
  134. package/src/lib/currency.ts +0 -81
  135. package/src/lib/dap-descriptions.ts +0 -50
  136. package/src/lib/dap-itinerary-preview.ts +0 -315
  137. package/src/lib/dependent-add-on-api.ts +0 -434
  138. package/src/lib/env.ts +0 -96
  139. package/src/lib/firebase.ts +0 -20
  140. package/src/lib/job-application-api.ts +0 -83
  141. package/src/lib/manage-booking-embed-print.ts +0 -16
  142. package/src/lib/manage-booking-post-checkout.ts +0 -68
  143. package/src/lib/photo-dap-config.ts +0 -228
  144. package/src/lib/photo-packages.ts +0 -75
  145. package/src/lib/pickup/map-utils.ts +0 -56
  146. package/src/lib/pickup/marker-icons.ts +0 -19
  147. package/src/lib/product-descriptions.ts +0 -66
  148. package/src/lib/products-config.ts +0 -73
  149. package/src/providers/booking-dialog-provider.tsx +0 -282
  150. package/src/providers/dependent-add-on-dialog-provider.tsx +0 -105
  151. package/src/radius.css +0 -5
  152. package/src/spacing.css +0 -7
  153. package/src/strings/en.json +0 -1774
  154. package/src/strings/es.json +0 -1573
  155. package/src/strings/fr.json +0 -1573
  156. package/src/strings/index.js +0 -23
  157. package/src/text-style.css +0 -97
  158. package/src/utils/currency-converter.ts +0 -101
@@ -1,153 +0,0 @@
1
- /**
2
- * Google Maps utilities
3
- */
4
-
5
- import type { Coordinates } from '@/lib/booking/location-utils';
6
- import type { PickupLocation } from '@/lib/booking-api';
7
- import type { NearbyLocation } from '@/lib/booking/pickup-location-types';
8
-
9
- export interface MapCenter {
10
- lat: number;
11
- lng: number;
12
- }
13
-
14
- const DEFAULT_CENTER: MapCenter = { lat: 51.1784, lng: -115.5708 };
15
-
16
- /**
17
- * Calculate map center based on available locations
18
- */
19
- export function calculateMapCenter(
20
- searchedLocation: { coordinates: Coordinates | null } | null,
21
- nearbyLocations: NearbyLocation[],
22
- pickupLocations: PickupLocation[]
23
- ): MapCenter {
24
- // Prioritize searched location - center on it when found
25
- if (searchedLocation?.coordinates) {
26
- return searchedLocation.coordinates;
27
- }
28
-
29
- if (nearbyLocations.length > 0 && nearbyLocations[0].coordinates) {
30
- return {
31
- lat: nearbyLocations[0].coordinates.lat,
32
- lng: nearbyLocations[0].coordinates.lng,
33
- };
34
- }
35
-
36
- // Default to first pickup location if available
37
- if (pickupLocations.length > 0 && pickupLocations[0].coordinates) {
38
- return {
39
- lat: pickupLocations[0].coordinates.lat,
40
- lng: pickupLocations[0].coordinates.lng,
41
- };
42
- }
43
-
44
- return DEFAULT_CENTER;
45
- }
46
-
47
- /**
48
- * Get map options configuration
49
- * Note: Returns a plain object that will be typed by Google Maps API
50
- */
51
- export function getMapOptions(): google.maps.MapOptions {
52
- return {
53
- disableDefaultUI: false,
54
- clickableIcons: false,
55
- scrollwheel: true,
56
- zoomControl: true,
57
- streetViewControl: false,
58
- mapTypeControl: false,
59
- fullscreenControl: true,
60
- gestureHandling: 'cooperative',
61
- panControl: false,
62
- } as google.maps.MapOptions;
63
- }
64
-
65
- /**
66
- * Calculate bounds for all locations to fit on map
67
- * Note: Requires Google Maps API to be loaded
68
- */
69
- export function calculateMapBounds(
70
- nearbyLocations: NearbyLocation[],
71
- searchedLocation: { coordinates: Coordinates | null } | null,
72
- isValidLocation: boolean | null,
73
- pickupLocations: PickupLocation[]
74
- ): google.maps.LatLngBounds | null {
75
- if (typeof google === 'undefined' || !google.maps) {
76
- console.warn('Google Maps API not loaded');
77
- return null;
78
- }
79
-
80
- try {
81
- const bounds = new google.maps.LatLngBounds();
82
- let hasLocations = false;
83
-
84
- // Add nearby locations to bounds (if user searched)
85
- nearbyLocations.forEach((location) => {
86
- if (location.coordinates) {
87
- bounds.extend(location.coordinates);
88
- hasLocations = true;
89
- }
90
- });
91
-
92
- // Add searched location to bounds if invalid
93
- if (searchedLocation?.coordinates && !isValidLocation) {
94
- bounds.extend(searchedLocation.coordinates);
95
- hasLocations = true;
96
- }
97
-
98
- // If no search results, show all pickup locations
99
- if (!hasLocations) {
100
- pickupLocations.forEach((location) => {
101
- if (location.coordinates) {
102
- bounds.extend(location.coordinates);
103
- hasLocations = true;
104
- }
105
- });
106
- }
107
-
108
- return hasLocations ? bounds : null;
109
- } catch (error) {
110
- console.error('Error calculating map bounds:', error);
111
- return null;
112
- }
113
- }
114
-
115
- /**
116
- * Pan map to location if it's outside viewport
117
- * Note: Requires Google Maps API to be loaded
118
- */
119
- export function panToLocationIfNeeded(
120
- map: google.maps.Map,
121
- coordinates: Coordinates
122
- ): void {
123
- if (typeof google === 'undefined' || !google.maps) {
124
- console.warn('Google Maps API not loaded');
125
- return;
126
- }
127
-
128
- try {
129
- const latLng = new google.maps.LatLng(coordinates.lat, coordinates.lng);
130
- const bounds = map.getBounds();
131
-
132
- if (bounds && !bounds.contains(latLng)) {
133
- const currentCenter = map.getCenter();
134
- if (currentCenter) {
135
- const panBounds = new google.maps.LatLngBounds();
136
- panBounds.extend(currentCenter);
137
- panBounds.extend(latLng);
138
- // Add padding to make the pan more visible
139
- map.fitBounds(panBounds, {
140
- top: 50,
141
- right: 50,
142
- bottom: 50,
143
- left: 50,
144
- });
145
- } else {
146
- map.panTo(latLng);
147
- }
148
- }
149
- } catch (error) {
150
- console.error('Error panning map:', error);
151
- }
152
- }
153
-
@@ -1,113 +0,0 @@
1
- /**
2
- * Marker icon generation utilities
3
- */
4
-
5
- export interface MarkerIconOptions {
6
- bgColor: string;
7
- distanceStr?: string;
8
- walkingTimeStr?: string;
9
- label?: string;
10
- }
11
-
12
- /**
13
- * Escape text for safe use in SVG
14
- */
15
- function escapeSvgText(text: string): string {
16
- return text
17
- .replace(/&/g, '&')
18
- .replace(/</g, '&lt;')
19
- .replace(/>/g, '&gt;')
20
- .replace(/"/g, '&quot;')
21
- .replace(/'/g, '&#39;');
22
- }
23
-
24
- /**
25
- * Validate and sanitize color value (hex color)
26
- */
27
- function validateColor(color: string): string {
28
- // Only allow hex colors (# followed by 3 or 6 hex digits)
29
- const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
30
- if (hexColorRegex.test(color)) {
31
- return color;
32
- }
33
- // Default to a safe color if invalid
34
- console.warn(`Invalid color provided: ${color}, using default`);
35
- return '#dc2626';
36
- }
37
-
38
- /**
39
- * Truncate text to fit within marker width
40
- */
41
- function truncateText(text: string, maxLength: number = 20): string {
42
- if (text.length <= maxLength) return text;
43
- return text.slice(0, maxLength - 3) + '...';
44
- }
45
-
46
- /**
47
- * Generate SVG icon for pickup location marker with distance/time
48
- */
49
- export function createDistanceMarkerIcon(options: MarkerIconOptions): string {
50
- const { bgColor, distanceStr, walkingTimeStr } = options;
51
- const safeColor = validateColor(bgColor);
52
- const text = distanceStr && walkingTimeStr
53
- ? truncateText(`${distanceStr} • ${walkingTimeStr}`, 20)
54
- : '';
55
- const escapedText = escapeSvgText(text);
56
-
57
- return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(`
58
- <svg width="120" height="32" viewBox="0 0 120 32" xmlns="http://www.w3.org/2000/svg">
59
- <rect x="0" y="0" width="120" height="24" rx="4" fill="${safeColor}"/>
60
- <polygon points="55,24 60,32 65,24" fill="${safeColor}"/>
61
- <text x="60" y="16" fill="white" font-size="10" font-weight="600" text-anchor="middle" font-family="Arial">${escapedText}</text>
62
- </svg>
63
- `)}`;
64
- }
65
-
66
- /**
67
- * Generate SVG icon for simple pickup location pin
68
- */
69
- export function createPinMarkerIcon(color: string): string {
70
- const safeColor = validateColor(color);
71
- return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(`
72
- <svg width="32" height="40" viewBox="0 0 32 40" fill="none" xmlns="http://www.w3.org/2000/svg">
73
- <path d="M16 0C7.163 0 0 7.163 0 16C0 24.837 16 40 16 40C16 40 32 24.837 32 16C32 7.163 24.837 0 16 0Z" fill="#ffffff"/>
74
- <path d="M16 2C8.268 2 2 8.268 2 16C2 22.177 16 38 16 38C16 38 30 22.177 30 16C30 8.268 23.732 2 16 2Z" fill="${safeColor}"/>
75
- <circle cx="16" cy="16" r="6" fill="#ffffff"/>
76
- </svg>
77
- `)}`;
78
- }
79
-
80
- /**
81
- * Generate SVG icon for user's searched location pin
82
- */
83
- export function createSearchedLocationPinIcon(): string {
84
- return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(`
85
- <svg width="32" height="40" viewBox="0 0 32 40" fill="none" xmlns="http://www.w3.org/2000/svg">
86
- <path d="M16 0C7.163 0 0 7.163 0 16C0 24.837 16 40 16 40C16 40 32 24.837 32 16C32 7.163 24.837 0 16 0Z" fill="#ffffff"/>
87
- <path d="M16 2C8.268 2 2 8.268 2 16C2 22.177 16 38 16 38C16 38 30 22.177 30 16C30 8.268 23.732 2 16 2Z" fill="#3b82f6"/>
88
- <circle cx="16" cy="16" r="6" fill="#ffffff"/>
89
- </svg>
90
- `)}`;
91
- }
92
-
93
- /**
94
- * Generate SVG icon for destination marker pin with text below (yellow)
95
- */
96
- export function createDestinationMarkerIcon(name: string, color: string = '#facc15'): string {
97
- const safeColor = validateColor(color);
98
- const escapedName = escapeSvgText(truncateText(name, 20));
99
- // SVG includes pin (40px height) + text below (20px height) = 60px total
100
- return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(`
101
- <svg width="80" height="60" viewBox="0 0 80 60" fill="none" xmlns="http://www.w3.org/2000/svg">
102
- <!-- Pin marker (centered horizontally, at top) -->
103
- <g transform="translate(24, 0)">
104
- <path d="M16 0C7.163 0 0 7.163 0 16C0 24.837 16 40 16 40C16 40 32 24.837 32 16C32 7.163 24.837 0 16 0Z" fill="#ffffff"/>
105
- <path d="M16 2C8.268 2 2 8.268 2 16C2 22.177 16 38 16 38C16 38 30 22.177 30 16C30 8.268 23.732 2 16 2Z" fill="${safeColor}"/>
106
- <circle cx="16" cy="16" r="6" fill="#ffffff"/>
107
- </g>
108
- <!-- Text below pin -->
109
- <text x="40" y="52" fill="#1f2937" font-size="12" font-weight="600" text-anchor="middle" font-family="Arial, sans-serif">${escapedName}</text>
110
- </svg>
111
- `)}`;
112
- }
113
-
@@ -1,21 +0,0 @@
1
- /**
2
- * Normalize IDs used in TicketBooth query params (product id, product option id).
3
- * Strips hash fragments, accidental query strings, or concatenated `&…` tails — aligned with
4
- * defensive parsing in GetAvailabilitiesHandler.
5
- */
6
- export function normalizeBookingProductId(rawId: string): string {
7
- const trimmed = rawId.trim();
8
- if (!trimmed) return '';
9
- const withoutHash = trimmed.split('#', 1)[0] ?? '';
10
- const withoutQuery = withoutHash.split('?', 1)[0] ?? '';
11
- const normalized = withoutQuery.split('&', 1)[0] ?? '';
12
- return normalized.trim();
13
- }
14
-
15
- /** True when the raw value clearly contains URL/query junk and should be normalized before use. */
16
- export function isSuspiciousBookingProductId(value: string): boolean {
17
- return /[?&=]/.test(value);
18
- }
19
-
20
- /** Alias: same normalization for get-availabilities lookup id (product or option). */
21
- export const normalizeAvailabilityLookupId = normalizeBookingProductId;
@@ -1,25 +0,0 @@
1
- /**
2
- * Types for pickup location selector
3
- */
4
-
5
- import type { PickupLocation } from '@/lib/booking-api';
6
- import type { Coordinates } from '@/lib/booking/location-utils';
7
-
8
- export interface NearbyLocation extends PickupLocation {
9
- distance: number;
10
- walkingTime: number;
11
- drivingTime: number;
12
- }
13
-
14
- export interface SearchedLocation {
15
- address: string;
16
- coordinates: Coordinates | null;
17
- }
18
-
19
- export interface AutocompleteSuggestion {
20
- placeId: string;
21
- mainText: string;
22
- secondaryText: string;
23
- description: string;
24
- }
25
-
@@ -1,154 +0,0 @@
1
- /**
2
- * Google Places API (New) utilities
3
- */
4
-
5
- export interface AutocompleteSuggestion {
6
- placeId: string;
7
- mainText: string;
8
- secondaryText: string;
9
- description: string;
10
- }
11
-
12
- // Internal API response types
13
- interface PlacePrediction {
14
- placeId: string;
15
- text?: { text: string };
16
- structuredFormat?: {
17
- mainText?: { text: string };
18
- secondaryText?: { text: string };
19
- };
20
- }
21
-
22
- interface AutocompleteSuggestionResponse {
23
- placePrediction?: PlacePrediction;
24
- }
25
-
26
- const GOOGLE_PLACES_API_BASE = 'https://places.googleapis.com/v1';
27
-
28
- /**
29
- * Get autocomplete suggestions using Places API (New)
30
- */
31
- export async function getAutocompleteSuggestions(
32
- input: string,
33
- apiKey: string,
34
- locationBias?: { latitude: number; longitude: number; radius: number }
35
- ): Promise<AutocompleteSuggestion[]> {
36
- if (!input.trim() || !apiKey) {
37
- return [];
38
- }
39
-
40
- try {
41
- const response = await fetch(`${GOOGLE_PLACES_API_BASE}/places:autocomplete`, {
42
- method: 'POST',
43
- headers: {
44
- 'Content-Type': 'application/json',
45
- 'X-Goog-Api-Key': apiKey,
46
- 'X-Goog-FieldMask':
47
- 'suggestions.placePrediction.placeId,suggestions.placePrediction.text,suggestions.placePrediction.structuredFormat',
48
- },
49
- body: JSON.stringify({
50
- input,
51
- includedRegionCodes: ['ca'], // Restrict to Canada
52
- ...(locationBias && {
53
- locationBias: {
54
- circle: {
55
- center: {
56
- latitude: locationBias.latitude,
57
- longitude: locationBias.longitude,
58
- },
59
- radius: locationBias.radius,
60
- },
61
- },
62
- }),
63
- }),
64
- });
65
-
66
- if (!response.ok) {
67
- const errorText = await response.text();
68
- console.error('Autocomplete API error:', response.status, errorText);
69
- throw new Error(`Autocomplete request failed: ${response.status} ${errorText}`);
70
- }
71
-
72
- const data = await response.json() as {
73
- suggestions?: AutocompleteSuggestionResponse[];
74
- };
75
-
76
- if (data.suggestions && Array.isArray(data.suggestions)) {
77
- return data.suggestions
78
- .filter((s): s is AutocompleteSuggestionResponse & { placePrediction: PlacePrediction } =>
79
- !!s.placePrediction
80
- )
81
- .map((s) => {
82
- const prediction = s.placePrediction;
83
- const text = prediction.text?.text || '';
84
- const structuredFormat = prediction.structuredFormat || {};
85
-
86
- return {
87
- placeId: prediction.placeId,
88
- mainText: structuredFormat.mainText?.text || text,
89
- secondaryText: structuredFormat.secondaryText?.text || '',
90
- description: text,
91
- };
92
- });
93
- }
94
-
95
- return [];
96
- } catch (error) {
97
- console.error('Autocomplete error:', error);
98
- return [];
99
- }
100
- }
101
-
102
- /**
103
- * Get place coordinates and types using Place Details API (New)
104
- */
105
- export async function getPlaceDetails(
106
- placeId: string,
107
- apiKey: string
108
- ): Promise<{ lat: number; lng: number; types: string[] } | null> {
109
- try {
110
- const response = await fetch(`${GOOGLE_PLACES_API_BASE}/places/${placeId}`, {
111
- method: 'GET',
112
- headers: {
113
- 'Content-Type': 'application/json',
114
- 'X-Goog-Api-Key': apiKey,
115
- 'X-Goog-FieldMask': 'location,types',
116
- },
117
- });
118
-
119
- if (!response.ok) {
120
- throw new Error('Place details request failed');
121
- }
122
-
123
- const data = await response.json();
124
-
125
- if (data.location) {
126
- return {
127
- lat: data.location.latitude,
128
- lng: data.location.longitude,
129
- types: data.types || [],
130
- };
131
- }
132
-
133
- return null;
134
- } catch (error) {
135
- console.error('Error getting place details:', error);
136
- return null;
137
- }
138
- }
139
-
140
- /**
141
- * Get place coordinates using Place Details API (New)
142
- * @deprecated Use getPlaceDetails instead to also get types
143
- */
144
- export async function getPlaceCoordinates(
145
- placeId: string,
146
- apiKey: string
147
- ): Promise<{ lat: number; lng: number } | null> {
148
- const details = await getPlaceDetails(placeId, apiKey);
149
- if (details) {
150
- return { lat: details.lat, lng: details.lng };
151
- }
152
- return null;
153
- }
154
-