@navigoo/map-components 1.0.0

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 (38) hide show
  1. package/README.md +0 -0
  2. package/dist/components/Dashboard.d.ts +14 -0
  3. package/dist/components/Dashboard.js +52 -0
  4. package/dist/components/DetourRouteSearch.d.ts +10 -0
  5. package/dist/components/DetourRouteSearch.js +119 -0
  6. package/dist/components/MapView.d.ts +14 -0
  7. package/dist/components/MapView.js +179 -0
  8. package/dist/components/RouteSearch.d.ts +10 -0
  9. package/dist/components/RouteSearch.js +95 -0
  10. package/dist/components/SearchBar.d.ts +10 -0
  11. package/dist/components/SearchBar.js +48 -0
  12. package/dist/components/TransportOptions.d.ts +3 -0
  13. package/dist/components/TransportOptions.js +24 -0
  14. package/dist/components/TripType.d.ts +3 -0
  15. package/dist/components/TripType.js +20 -0
  16. package/dist/index.d.ts +10 -0
  17. package/dist/index.js +39 -0
  18. package/dist/lib/api.d.ts +21 -0
  19. package/dist/lib/api.js +58 -0
  20. package/dist/lib/geolocalisation.d.ts +9 -0
  21. package/dist/lib/geolocalisation.js +30 -0
  22. package/dist/lib/type.d.ts +32 -0
  23. package/dist/lib/type.js +2 -0
  24. package/package.json +31 -0
  25. package/src/components/Dashboard.tsx +183 -0
  26. package/src/components/DetourRouteSearch.tsx +217 -0
  27. package/src/components/MapView.tsx +201 -0
  28. package/src/components/RouteSearch.tsx +163 -0
  29. package/src/components/SearchBar.tsx +87 -0
  30. package/src/components/TransportOptions.tsx +53 -0
  31. package/src/components/TripType.tsx +47 -0
  32. package/src/components/styles.modules.css +207 -0
  33. package/src/components/wellknown.d.ts +1 -0
  34. package/src/css-module.d.ts +4 -0
  35. package/src/index.ts +10 -0
  36. package/src/lib/api.ts +74 -0
  37. package/src/lib/geolocalisation.ts +41 -0
  38. package/src/lib/type.ts +37 -0
@@ -0,0 +1,201 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import L from 'leaflet';
3
+ import 'leaflet/dist/leaflet.css';
4
+ import { parse } from 'wellknown';
5
+ import { Place, Route, GeolocationResult } from '../lib/type';
6
+ import { ApiClient } from '../lib/api';
7
+
8
+ interface MapViewProps {
9
+ apiClient: ApiClient;
10
+ userLocation?: GeolocationResult | null;
11
+ searchedPlace?: Place | null;
12
+ routes?: Route[];
13
+ selectedRouteIndex: number;
14
+ setSelectedRouteIndex: (index: number) => void;
15
+ }
16
+
17
+ const MapView: React.FC<MapViewProps> = ({
18
+ apiClient,
19
+ userLocation,
20
+ searchedPlace,
21
+ routes,
22
+ selectedRouteIndex,
23
+ setSelectedRouteIndex,
24
+ }) => {
25
+ const mapRef = useRef<L.Map | null>(null);
26
+ const mapContainerRef = useRef<HTMLDivElement>(null);
27
+ const markerRef = useRef<L.Marker | null>(null);
28
+ const routeLayerRef = useRef<L.LayerGroup | null>(null);
29
+ const clickMarkerRef = useRef<L.Marker | null>(null);
30
+ const routePolylinesRef = useRef<L.Polyline[]>([]);
31
+
32
+ const parseWKTLineString = (wkt: string): [number, number][] => {
33
+ try {
34
+ const geo = parse(wkt);
35
+ if (geo && geo.type === 'LineString' && Array.isArray(geo.coordinates)) {
36
+ return geo.coordinates.map(([lng, lat]: [number, number]) => [lat, lng] as [number, number]);
37
+ }
38
+ } catch (error) {
39
+ console.error('wellknown parsing failed:', error);
40
+ }
41
+ const match = wkt.match(/LINESTRING\s*\(([^)]+)\)/);
42
+ if (match) {
43
+ return match[1]
44
+ .split(',')
45
+ .map(coord => {
46
+ const [lng, lat] = coord.trim().split(' ').map(Number);
47
+ return [lat, lng] as [number, number];
48
+ });
49
+ }
50
+ return [];
51
+ };
52
+
53
+ useEffect(() => {
54
+ if (mapContainerRef.current && !mapRef.current) {
55
+ mapRef.current = L.map(mapContainerRef.current, {
56
+ center: [3.8480, 11.5021], // Centre par défaut : Yaoundé
57
+ zoom: 12,
58
+ minZoom: 11,
59
+ maxZoom: 16,
60
+ maxBounds: [[3.7, 11.4], [4.0, 11.6]],
61
+ maxBoundsViscosity: 1.0,
62
+ });
63
+
64
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
65
+ attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
66
+ maxZoom: 16,
67
+ }).addTo(mapRef.current);
68
+
69
+ L.Icon.Default.mergeOptions({
70
+ iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
71
+ iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
72
+ shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
73
+ });
74
+
75
+ routeLayerRef.current = L.layerGroup().addTo(mapRef.current);
76
+
77
+ mapRef.current.on('click', async (e: L.LeafletMouseEvent) => {
78
+ const { lat, lng } = e.latlng;
79
+ const closestPlace = await apiClient.findClosestPlace(lat, lng);
80
+ const placeName = closestPlace?.name || 'Position sélectionnée';
81
+
82
+ if (clickMarkerRef.current) {
83
+ mapRef.current?.removeLayer(clickMarkerRef.current);
84
+ clickMarkerRef.current = null;
85
+ } else {
86
+ clickMarkerRef.current = L.marker([lat, lng])
87
+ .addTo(mapRef.current!)
88
+ .bindPopup(`<b>${placeName}</b><br>Lat: ${lat.toFixed(6)}<br>Lng: ${lng.toFixed(6)}`)
89
+ .openPopup();
90
+ }
91
+ });
92
+ }
93
+
94
+ return () => {
95
+ if (mapRef.current) {
96
+ mapRef.current.remove();
97
+ mapRef.current = null;
98
+ }
99
+ };
100
+ }, [apiClient]);
101
+
102
+ useEffect(() => {
103
+ if (!mapRef.current) return;
104
+
105
+ if (routeLayerRef.current) routeLayerRef.current.clearLayers();
106
+ if (markerRef.current) markerRef.current.remove();
107
+ if (clickMarkerRef.current) clickMarkerRef.current.remove();
108
+ routePolylinesRef.current = [];
109
+
110
+ const centerOnPoint = async (lat: number, lng: number, placeName: string, zoom: number = 16) => {
111
+ let displayName = placeName;
112
+ if (placeName === 'Votre position') {
113
+ const closestPlace = await apiClient.findClosestPlace(lat, lng);
114
+ displayName = closestPlace?.name || placeName;
115
+ }
116
+ mapRef.current!.setView([lat, lng], zoom, { animate: true });
117
+ markerRef.current = L.marker([lat, lng])
118
+ .addTo(mapRef.current!)
119
+ .bindPopup(`<b>${displayName}</b><br>Lat: ${lat.toFixed(6)}<br>Lng: ${lng.toFixed(6)}`)
120
+ .openPopup();
121
+ };
122
+
123
+ if (routes && routes.length > 0) {
124
+ let allCoordinates: [number, number][] = [];
125
+ routes.forEach((route, index) => {
126
+ const coordinates: [number, number][] = [];
127
+ route.steps.forEach((step) => {
128
+ const latLngs = parseWKTLineString(step.geometry);
129
+ if (latLngs.length > 0) coordinates.push(...latLngs);
130
+ });
131
+
132
+ if (coordinates.length > 0) {
133
+ const color = index === selectedRouteIndex ? 'green' : 'black';
134
+ const weight = index === selectedRouteIndex ? 5 : 3;
135
+ const opacity = index === selectedRouteIndex ? 1.0 : 0.5;
136
+
137
+ const polyline = L.polyline(coordinates, { color, weight, opacity })
138
+ .addTo(routeLayerRef.current!)
139
+ .on('click', (e: L.LeafletMouseEvent) => {
140
+ L.DomEvent.stopPropagation(e);
141
+ setSelectedRouteIndex(index);
142
+ routePolylinesRef.current.forEach((pl, i) => {
143
+ pl.setStyle({
144
+ color: i === index ? 'green' : 'black',
145
+ weight: i === index ? 5 : 3,
146
+ opacity: i === index ? 1.0 : 0.5,
147
+ });
148
+ });
149
+ const bounds = polyline.getBounds();
150
+ const center = bounds.getCenter();
151
+ L.popup()
152
+ .setLatLng(center)
153
+ .setContent(`
154
+ <b>Route ${index + 1}</b><br>
155
+ Distance: ${route.distance.toFixed(2)} m<br>
156
+ Durée: ${(route.duration / 60).toFixed(2)} min<br>
157
+ Départ: ${route.startPlaceName || 'Départ'}<br>
158
+ Destination: ${route.endPlaceName || 'Destination'}
159
+ `)
160
+ .openOn(mapRef.current!);
161
+ });
162
+
163
+ routePolylinesRef.current.push(polyline);
164
+ allCoordinates = [...allCoordinates, ...coordinates];
165
+
166
+ if (index === selectedRouteIndex) {
167
+ const startPoint = coordinates[0];
168
+ const endPoint = coordinates[coordinates.length - 1];
169
+ (async () => {
170
+ let startPlaceName = route.startPlaceName || 'Départ';
171
+ if (route.startPlaceName === 'Votre position') {
172
+ const closestStartPlace = await apiClient.findClosestPlace(startPoint[1], startPoint[0]);
173
+ startPlaceName = closestStartPlace?.name || route.startPlaceName;
174
+ }
175
+ L.marker(startPoint).addTo(routeLayerRef.current!).bindPopup(`
176
+ <b>${startPlaceName}</b><br>Lat: ${startPoint[0].toFixed(6)}<br>Lng: ${startPoint[1].toFixed(6)}
177
+ `);
178
+ L.marker(endPoint).addTo(routeLayerRef.current!).bindPopup(`
179
+ <b>${route.endPlaceName || 'Destination'}</b><br>Lat: ${endPoint[0].toFixed(6)}<br>Lng: ${endPoint[1].toFixed(6)}
180
+ `);
181
+ })();
182
+ }
183
+ }
184
+ });
185
+
186
+ if (allCoordinates.length > 0) {
187
+ mapRef.current!.fitBounds(L.latLngBounds(allCoordinates));
188
+ }
189
+ } else if (searchedPlace && searchedPlace.coordinates) {
190
+ centerOnPoint(searchedPlace.coordinates.lat, searchedPlace.coordinates.lng, searchedPlace.name);
191
+ } else if (userLocation) {
192
+ centerOnPoint(userLocation.latitude, userLocation.longitude, 'Votre position');
193
+ } else {
194
+ mapRef.current!.setView([3.8480, 11.5021], 12, { animate: true });
195
+ }
196
+ }, [apiClient, userLocation, searchedPlace, routes, selectedRouteIndex, setSelectedRouteIndex]);
197
+
198
+ return <div className="w-full h-screen" ref={mapContainerRef} />;
199
+ };
200
+
201
+ export default MapView;
@@ -0,0 +1,163 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Place, Route, GeolocationResult } from '../lib/type';
3
+ import { ApiClient } from '../lib/api';
4
+ import styles from './styles.module.css';
5
+
6
+ interface RouteSearchProps {
7
+ apiClient: ApiClient;
8
+ setRoutes: (routes: Route[]) => void;
9
+ setSelectedRouteIndex: (index: number) => void;
10
+ }
11
+
12
+ const RouteSearch: React.FC<RouteSearchProps> = ({ apiClient, setRoutes, setSelectedRouteIndex }) => {
13
+ const [startQuery, setStartQuery] = useState('');
14
+ const [endQuery, setEndQuery] = useState('');
15
+ const [startResults, setStartResults] = useState<Place[]>([]);
16
+ const [endResults, setEndResults] = useState<Place[]>([]);
17
+ const [selectedStart, setSelectedStart] = useState<Place | null>(null);
18
+ const [selectedEnd, setSelectedEnd] = useState<Place | null>(null);
19
+ const [loading, setLoading] = useState(false);
20
+ const [error, setError] = useState<string | null>(null);
21
+ const [transportMode, setTransportMode] = useState<'driving' | 'walking' | 'cycling'>('driving');
22
+
23
+ useEffect(() => {
24
+ const handleTransportChange = (e: Event) => {
25
+ const customEvent = e as CustomEvent;
26
+ if (customEvent.detail === 'taxi' || customEvent.detail === 'bus') {
27
+ setTransportMode('driving');
28
+ } else if (customEvent.detail === 'moto') {
29
+ setTransportMode('cycling');
30
+ }
31
+ };
32
+ window.addEventListener('transportChange', handleTransportChange);
33
+ return () => window.removeEventListener('transportChange', handleTransportChange);
34
+ }, []);
35
+
36
+ const handleSearch = async (query: string, setResults: (results: Place[]) => void) => {
37
+ if (!query) return;
38
+ setLoading(true);
39
+ setError(null);
40
+ try {
41
+ const places = await apiClient.searchPlaces(query);
42
+ setResults(places);
43
+ if (places.length === 0) {
44
+ setError('Aucun lieu trouvé');
45
+ }
46
+ } catch (err) {
47
+ setError('Erreur lors de la recherche');
48
+ } finally {
49
+ setLoading(false);
50
+ }
51
+ };
52
+
53
+ const handleSelectStart = (place: Place) => {
54
+ setStartQuery(place.name);
55
+ setSelectedStart(place);
56
+ setStartResults([]);
57
+ setRoutes([]);
58
+ setSelectedRouteIndex(0);
59
+ };
60
+
61
+ const handleSelectEnd = (place: Place) => {
62
+ setEndQuery(place.name);
63
+ setSelectedEnd(place);
64
+ setEndResults([]);
65
+ setRoutes([]);
66
+ setSelectedRouteIndex(0);
67
+ };
68
+
69
+ const handleCalculateRoute = async () => {
70
+ if (!selectedStart || !selectedEnd || !selectedStart.coordinates || !selectedEnd.coordinates) {
71
+ setError('Veuillez sélectionner un point de départ et une destination');
72
+ return;
73
+ }
74
+ setLoading(true);
75
+ setError(null);
76
+ try {
77
+ const routes = await apiClient.calculateRoute(
78
+ [
79
+ { lat: selectedStart.coordinates.lat, lng: selectedStart.coordinates.lng },
80
+ { lat: selectedEnd.coordinates.lat, lng: selectedEnd.coordinates.lng },
81
+ ],
82
+ transportMode,
83
+ selectedStart.name,
84
+ selectedEnd.name
85
+ );
86
+ setRoutes(routes);
87
+ setSelectedRouteIndex(0);
88
+ } catch (err) {
89
+ setError('Erreur lors du calcul de l\'itinéraire');
90
+ } finally {
91
+ setLoading(false);
92
+ }
93
+ };
94
+
95
+ return (
96
+ <div className={styles.routeSearchSection}>
97
+ <div className={styles.searchGroup}>
98
+ <label htmlFor="start" className={styles.label}>Départ</label>
99
+ <input
100
+ id="start"
101
+ type="text"
102
+ value={startQuery}
103
+ onChange={(e) => {
104
+ setStartQuery(e.target.value);
105
+ handleSearch(e.target.value, setStartResults);
106
+ }}
107
+ placeholder="Point de départ"
108
+ className={styles.searchInput}
109
+ />
110
+ {startResults.length > 0 && (
111
+ <ul className={styles.searchResults}>
112
+ {startResults.map((place) => (
113
+ <li
114
+ key={place.id}
115
+ onClick={() => handleSelectStart(place)}
116
+ className={styles.searchResultItem}
117
+ >
118
+ {place.name}
119
+ </li>
120
+ ))}
121
+ </ul>
122
+ )}
123
+ </div>
124
+ <div className={styles.searchGroup}>
125
+ <label htmlFor="end" className={styles.label}>Destination</label>
126
+ <input
127
+ id="end"
128
+ type="text"
129
+ value={endQuery}
130
+ onChange={(e) => {
131
+ setEndQuery(e.target.value);
132
+ handleSearch(e.target.value, setEndResults);
133
+ }}
134
+ placeholder="Point d'arrivée"
135
+ className={styles.searchInput}
136
+ />
137
+ {endResults.length > 0 && (
138
+ <ul className={styles.searchResults}>
139
+ {endResults.map((place) => (
140
+ <li
141
+ key={place.id}
142
+ onClick={() => handleSelectEnd(place)}
143
+ className={styles.searchResultItem}
144
+ >
145
+ {place.name}
146
+ </li>
147
+ ))}
148
+ </ul>
149
+ )}
150
+ </div>
151
+ {error && <div className={styles.error}>{error}</div>}
152
+ <button
153
+ onClick={handleCalculateRoute}
154
+ disabled={loading || !selectedStart || !selectedEnd}
155
+ className={`${styles.searchButton} ${loading || !selectedStart || !selectedEnd ? styles.disabled : ''}`}
156
+ >
157
+ {loading ? 'Calcul...' : 'Calculer l\'itinéraire'}
158
+ </button>
159
+ </div>
160
+ );
161
+ };
162
+
163
+ export default RouteSearch;
@@ -0,0 +1,87 @@
1
+ import React, { useState } from 'react';
2
+ import { Place, GeolocationResult } from '../lib/type';
3
+ import { ApiClient } from '../lib/api';
4
+ import styles from './styles.module.css';
5
+
6
+ interface SearchBarProps {
7
+ apiClient: ApiClient;
8
+ setUserLocation: (location: GeolocationResult | null) => void;
9
+ setSearchedPlace: (place: Place | null) => void;
10
+ }
11
+
12
+ const SearchBar: React.FC<SearchBarProps> = ({ apiClient, setUserLocation, setSearchedPlace }) => {
13
+ const [query, setQuery] = useState('');
14
+ const [loading, setLoading] = useState(false);
15
+ const [results, setResults] = useState<Place[]>([]);
16
+ const [error, setError] = useState<string | null>(null);
17
+
18
+ const handleSearch = async () => {
19
+ if (!query || loading) return;
20
+ setLoading(true);
21
+ setError(null);
22
+ setResults([]);
23
+ setSearchedPlace(null);
24
+
25
+ try {
26
+ const places = await apiClient.searchPlaces(query);
27
+ setResults(places);
28
+ if (places.length === 0) {
29
+ setError('Aucun lieu trouvé');
30
+ }
31
+ } catch (err) {
32
+ setError('Erreur lors de la recherche');
33
+ } finally {
34
+ setLoading(false);
35
+ }
36
+ };
37
+
38
+ const handleSelect = (place: Place) => {
39
+ setQuery(place.name);
40
+ if (place.coordinates) {
41
+ setUserLocation({ latitude: place.coordinates.lat, longitude: place.coordinates.lng });
42
+ setSearchedPlace(place);
43
+ } else {
44
+ setError('Lieu sans coordonnées valides');
45
+ }
46
+ setResults([]);
47
+ };
48
+
49
+ return (
50
+ <div className={styles.searchSection}>
51
+ <div className={styles.searchGroup}>
52
+ <label htmlFor="search" className={styles.label}>Nom du lieu</label>
53
+ <input
54
+ id="search"
55
+ type="text"
56
+ value={query}
57
+ onChange={(e) => setQuery(e.target.value)}
58
+ placeholder="Rechercher un lieu"
59
+ className={styles.searchInput}
60
+ />
61
+ </div>
62
+ {error && <div className={styles.error}>{error}</div>}
63
+ {results.length > 0 && (
64
+ <ul className={styles.searchResults}>
65
+ {results.map((place) => (
66
+ <li
67
+ key={place.id}
68
+ onClick={() => handleSelect(place)}
69
+ className={styles.searchResultItem}
70
+ >
71
+ {place.name}
72
+ </li>
73
+ ))}
74
+ </ul>
75
+ )}
76
+ <button
77
+ onClick={handleSearch}
78
+ disabled={loading || !query}
79
+ className={`${styles.searchButton} ${loading || !query ? styles.disabled : ''}`}
80
+ >
81
+ {loading ? 'Recherche...' : 'Rechercher'}
82
+ </button>
83
+ </div>
84
+ );
85
+ };
86
+
87
+ export default SearchBar;
@@ -0,0 +1,53 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import styles from './styles.module.css';
3
+
4
+ const TransportOptions: React.FC = () => {
5
+ const [showOptions, setShowOptions] = useState(false);
6
+ const [selectedTransport, setSelectedTransport] = useState('taxi');
7
+
8
+ const transportOptions = [
9
+ { id: 'taxi', name: 'Taxi', icon: '🚕', color: '#3498db' },
10
+ { id: 'bus', name: 'Bus', icon: '🚌', color: '#2980b9' },
11
+ { id: 'moto', name: 'Moto', icon: '🏍️', color: '#1abc9c' },
12
+ ];
13
+
14
+ useEffect(() => {
15
+ window.dispatchEvent(new CustomEvent('transportChange', { detail: selectedTransport }));
16
+ }, [selectedTransport]);
17
+
18
+ return (
19
+ <div className={styles.optionsSection}>
20
+ <div className={styles.optionsHeader}>
21
+ <h3 className={styles.optionsTitle}>Moyen de transport</h3>
22
+ <button
23
+ onClick={() => setShowOptions(!showOptions)}
24
+ className={styles.toggleButton}
25
+ >
26
+ {showOptions ? 'Réduire' : 'Options'}
27
+ </button>
28
+ </div>
29
+ {showOptions && (
30
+ <div className={styles.transportOptions}>
31
+ {transportOptions.map((option) => (
32
+ <button
33
+ key={option.id}
34
+ onClick={() => setSelectedTransport(option.id)}
35
+ className={`${styles.transportOption} ${
36
+ selectedTransport === option.id ? styles.selected : ''
37
+ }`}
38
+ style={{
39
+ backgroundColor: selectedTransport === option.id ? option.color : undefined,
40
+ }}
41
+ >
42
+ <span className={styles.transportIcon}>{option.icon}</span>
43
+ <span className={styles.transportName}>{option.name}</span>
44
+ </button>
45
+ ))}
46
+ </div>
47
+ )}
48
+ </div>
49
+
50
+ );
51
+ };
52
+
53
+ export default TransportOptions;
@@ -0,0 +1,47 @@
1
+ import React, { useState } from 'react';
2
+ import styles from './styles.module.css';
3
+
4
+ const TripType: React.FC = () => {
5
+ const [showOptions, setShowOptions] = useState(false);
6
+ const [selectedTripType, setSelectedTripType] = useState('individuel');
7
+
8
+ const tripTypeOptions = [
9
+ { id: 'individuel', name: 'Individuel', icon: '👤', color: '#3498db' },
10
+ { id: 'ramassage', name: 'Ramassage', icon: '👥', color: '#9b59b6' },
11
+ ];
12
+
13
+ return (
14
+ <div className={styles.optionsSection}>
15
+ <div className={styles.optionsHeader}>
16
+ <h3 className={styles.optionsTitle}>Type de trajet</h3>
17
+ <button
18
+ onClick={() => setShowOptions(!showOptions)}
19
+ className={styles.toggleButton}
20
+ >
21
+ {showOptions ? 'Réduire' : 'Options'}
22
+ </button>
23
+ </div>
24
+ {showOptions && (
25
+ <div className={styles.transportOptions}>
26
+ {tripTypeOptions.map((option) => (
27
+ <button
28
+ key={option.id}
29
+ onClick={() => setSelectedTripType(option.id)}
30
+ className={`${styles.transportOption} ${
31
+ selectedTripType === option.id ? styles.selected : ''
32
+ }`}
33
+ style={{
34
+ backgroundColor: selectedTripType === option.id ? option.color : undefined,
35
+ }}
36
+ >
37
+ <span className={styles.transportIcon}>{option.icon}</span>
38
+ <span className={styles.transportName}>{option.name}</span>
39
+ </button>
40
+ ))}
41
+ </div>
42
+ )}
43
+ </div>
44
+ );
45
+ };
46
+
47
+ export default TripType;