Skip to content

Commit dbbbc1d

Browse files
committed
feat(frontend): add configuration and confirm dialog components
- Introduced a new configuration file to manage API base URL and Mapbox token. - Added a ConfirmDialog component for consistent confirmation prompts across the application. - Updated MapView and other components to utilize the new hooks for improved modularity and maintainability. - Refactored imports in MapView to align with the new directory structure for hooks and utilities. This commit enhances the overall structure and user experience by providing reusable components and centralized configuration management.
1 parent 76879f2 commit dbbbc1d

29 files changed

+397
-670
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React from 'react'
2+
import {
3+
Modal,
4+
ModalOverlay,
5+
ModalContent,
6+
ModalHeader,
7+
ModalFooter,
8+
ModalBody,
9+
ModalCloseButton,
10+
Button,
11+
Text,
12+
Alert,
13+
AlertIcon,
14+
} from '@chakra-ui/react'
15+
16+
interface ConfirmDialogProps {
17+
isOpen: boolean
18+
onClose: () => void
19+
onConfirm: () => void
20+
title: string
21+
confirmLabel?: string
22+
severity?: 'warning' | 'info'
23+
body?: React.ReactNode
24+
nameHighlight?: string
25+
}
26+
27+
export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
28+
isOpen,
29+
onClose,
30+
onConfirm,
31+
title,
32+
confirmLabel = 'Confirm',
33+
severity = 'warning',
34+
body,
35+
nameHighlight,
36+
}) => (
37+
<Modal isOpen={isOpen} onClose={onClose}>
38+
<ModalOverlay />
39+
<ModalContent>
40+
<ModalHeader>{title}</ModalHeader>
41+
<ModalCloseButton />
42+
<ModalBody>
43+
<Alert status={severity} mb={4}>
44+
<AlertIcon />
45+
This action cannot be undone.
46+
</Alert>
47+
{body ?? (
48+
<Text>
49+
Are you sure you want to proceed
50+
{nameHighlight ? (
51+
<>
52+
{' '}
53+
with{' '}
54+
<Text as="span" fontWeight="bold">
55+
{nameHighlight}
56+
</Text>
57+
?
58+
</>
59+
) : (
60+
'?'
61+
)}
62+
</Text>
63+
)}
64+
</ModalBody>
65+
<ModalFooter>
66+
<Button variant="ghost" mr={3} onClick={onClose}>
67+
Cancel
68+
</Button>
69+
<Button colorScheme="red" onClick={onConfirm}>
70+
{confirmLabel}
71+
</Button>
72+
</ModalFooter>
73+
</ModalContent>
74+
</Modal>
75+
)

frontend/src/components/maps/MapControls.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
22
import { Box, Text, VStack, HStack, Switch, FormLabel } from '@chakra-ui/react'
3-
import { MapControls } from '../../types/map'
3+
import type { MapControls } from '../../types/map'
44

55
interface MapControlsProps {
66
controls: MapControls

frontend/src/components/maps/MapView.tsx

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import React, { useEffect, useState, useRef } from 'react'
22
import mapboxgl from 'mapbox-gl'
33
import { Box, Text, useDisclosure } from '@chakra-ui/react'
4-
import { useMapData } from '../../hooks/useMapData'
5-
import { useStopMarkers } from '../../hooks/useStopMarkers'
6-
import { useVehicleMarkers } from '../../hooks/useVehicleMarkers'
7-
import { useTripRoutes } from '../../hooks/useTripRoutes'
8-
import { useMapClickHandler } from '../../hooks/useMapClickHandler'
4+
import { useMapData } from './hooks/useMapData'
5+
import { useStopMarkers } from './hooks/useStopMarkers'
6+
import { useVehicleMarkers } from './hooks/useVehicleMarkers'
7+
import { useTripRoutes } from './hooks/useTripRoutes'
8+
import { useMapClickHandler } from './hooks/useMapClickHandler'
99
import { useAuth } from '../../contexts/AuthContext'
1010
import type { MapControls, Trip } from '../../types/map'
11-
import { getMapCustomCSS } from '../../utils/mapStyles'
11+
import { getMapCustomCSS } from './utils/mapStyles'
1212
import MapControlsComponent from './MapControls'
1313
import { TripDetailsDrawer } from '../trips/TripDetailsDrawer'
14+
import { get } from '../../lib/api'
1415
import 'mapbox-gl/dist/mapbox-gl.css'
1516

1617
const MapView: React.FC = () => {
@@ -37,22 +38,11 @@ const MapView: React.FC = () => {
3738
if (selectedTrip && token) {
3839
try {
3940
// Fetch the latest trip data
40-
const response = await fetch(
41-
`http://localhost:8000/api/trips/${selectedTrip.id}/`,
42-
{
43-
headers: {
44-
Authorization: `Token ${token}`,
45-
'Content-Type': 'application/json',
46-
},
47-
}
48-
)
49-
if (response.ok) {
50-
const updatedTrip = await response.json()
51-
// Update the selected trip state
52-
setSelectedTrip(updatedTrip)
53-
// Refresh just this trip's route on the map
54-
tripRoutes.refreshSelectedTrip(updatedTrip, controls.showTrips)
55-
}
41+
const updatedTrip = await get<Trip>(`/trips/${selectedTrip.id}/`)
42+
// Update the selected trip state
43+
setSelectedTrip(updatedTrip)
44+
// Refresh just this trip's route on the map
45+
tripRoutes.refreshSelectedTrip(updatedTrip, controls.showTrips)
5646
} catch (error) {
5747
console.error('Error fetching updated trip:', error)
5848
}

frontend/src/hooks/useMapClickHandler.ts renamed to frontend/src/components/maps/hooks/useMapClickHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { useCallback } from 'react'
2-
import mapboxgl from 'mapbox-gl'
2+
import type { Map } from 'mapbox-gl'
33

44
export const useMapClickHandler = (
5-
map: React.MutableRefObject<mapboxgl.Map | null>,
5+
map: React.MutableRefObject<Map | null>,
66
closeAllPopups: () => void
77
) => {
88
const setupMapClickHandler = useCallback(() => {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useState, useEffect, useCallback } from 'react'
2+
import type { Stop, Trip, VehiclePosition } from '../../../types/map'
3+
import { useAuth } from '../../../contexts/AuthContext'
4+
import { get } from '../../../lib/api'
5+
6+
export const useMapData = () => {
7+
const [stops, setStops] = useState<Stop[]>([])
8+
const [trips, setTrips] = useState<Trip[]>([])
9+
const [vehiclePositions, setVehiclePositions] = useState<VehiclePosition[]>(
10+
[]
11+
)
12+
const [loading, setLoading] = useState(true)
13+
const [error, setError] = useState<string | null>(null)
14+
15+
const { token } = useAuth()
16+
17+
const fetchStops = useCallback(async () => {
18+
try {
19+
setLoading(true)
20+
const stops = await get<Stop[]>('/stops/')
21+
const stopsWithCoords = stops.filter(
22+
(stop: Stop) => stop.latitude && stop.longitude
23+
)
24+
setStops(stopsWithCoords)
25+
} catch (error) {
26+
console.error('Error fetching stops:', error)
27+
setError('Failed to load stops')
28+
} finally {
29+
setLoading(false)
30+
}
31+
}, [])
32+
33+
const fetchTrips = useCallback(async () => {
34+
try {
35+
const trips = await get<Trip[]>('/trips/')
36+
setTrips(trips)
37+
} catch (error) {
38+
console.error('Error fetching trips:', error)
39+
}
40+
}, [])
41+
42+
const fetchVehiclePositions = useCallback(async () => {
43+
try {
44+
const positions = await get<VehiclePosition[]>('/positions/latest/')
45+
setVehiclePositions(positions)
46+
} catch (error) {
47+
console.error('Error fetching vehicle positions:', error)
48+
}
49+
}, [])
50+
51+
useEffect(() => {
52+
if (token) {
53+
fetchStops()
54+
fetchTrips()
55+
fetchVehiclePositions()
56+
}
57+
}, [token, fetchStops, fetchTrips, fetchVehiclePositions])
58+
59+
return {
60+
stops,
61+
trips,
62+
vehiclePositions,
63+
loading,
64+
error,
65+
refetchStops: fetchStops,
66+
refetchTrips: fetchTrips,
67+
refetchVehiclePositions: fetchVehiclePositions,
68+
}
69+
}

frontend/src/hooks/useMapLayers.ts renamed to frontend/src/components/maps/hooks/useMapLayers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useRef, useCallback } from 'react'
22
import mapboxgl from 'mapbox-gl'
3-
import { Stop, Trip, VehiclePosition } from '../types/map'
3+
import type { Stop, Trip, VehiclePosition } from '../../../types/map'
44
import {
55
createStopPopupContent,
66
createTripPopupContent,
File renamed without changes.

frontend/src/hooks/useStopMarkers.ts renamed to frontend/src/components/maps/hooks/useStopMarkers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useRef, useCallback } from 'react'
22
import mapboxgl from 'mapbox-gl'
3-
import type { Stop } from '../types/map'
3+
import type { Stop } from '../../../types/map'
44
import { createStopPopupContent } from '../utils/mapPopups'
55
import { createStopMarkerElement } from '../utils/mapStyles'
66
export const useStopMarkers = (

frontend/src/hooks/useTripRoutes.ts renamed to frontend/src/components/maps/hooks/useTripRoutes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useRef, useCallback } from 'react'
22
import mapboxgl from 'mapbox-gl'
3-
import type { Trip } from '../types/map'
3+
import type { Trip } from '../../../types/map'
44
import { createTripPopupContent } from '../utils/mapPopups'
55
import { getStatusColor } from '../utils/mapStyles'
66
export const useTripRoutes = (

frontend/src/hooks/useVehicleMarkers.ts renamed to frontend/src/components/maps/hooks/useVehicleMarkers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useRef, useCallback } from 'react'
22
import mapboxgl from 'mapbox-gl'
3-
import type { VehiclePosition } from '../types/map'
3+
import type { VehiclePosition } from '../../../types/map'
44
import { createVehiclePopupContent } from '../utils/mapPopups'
55
import { createVehicleArrowElement } from '../utils/mapStyles'
66
export const useVehicleMarkers = (

0 commit comments

Comments
 (0)