Skip to content

Commit 019d9f3

Browse files
committed
move logs to sidebar and make map live
1 parent 1457e7c commit 019d9f3

File tree

13 files changed

+642
-383
lines changed

13 files changed

+642
-383
lines changed

services/frontend/react_app/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
"framer-motion": "^11.0.8",
1717
"geojson-path-finder": "^2.0.2",
1818
"leaflet": "^1.9.4",
19+
"leaflet-routing-machine": "^3.2.12",
1920
"pigeon-maps": "^0.21.4",
2021
"react": "^18.2.0",
2122
"react-dom": "^18.2.0",
2223
"react-leaflet": "^4.2.1"
2324
},
2425
"devDependencies": {
25-
"@types/leaflet": "^1.9.8",
26+
"@types/leaflet": "^1.9.15",
27+
"@types/leaflet-routing-machine": "^3.2.8",
2628
"@types/react": "^18.2.56",
2729
"@types/react-dom": "^18.2.19",
2830
"@typescript-eslint/eslint-plugin": "^7.0.2",
Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,39 @@
11
import './App.css'
22
import {HomePage} from "./pages/home.tsx";
33
import {SessionProvider} from "./context/sessionContext/context.tsx";
4+
import {ChakraProvider, extendTheme} from "@chakra-ui/react";
45

56
function App() {
6-
return (
7-
<SessionProvider>
8-
<HomePage />
9-
</SessionProvider>
10-
)
7+
8+
const components = {
9+
Drawer: {
10+
variants: {
11+
aside: {
12+
overlay: {
13+
pointerEvents: 'none',
14+
background: 'transparent',
15+
},
16+
dialogContainer: {
17+
pointerEvents: 'none',
18+
background: 'transparent',
19+
},
20+
dialog: {
21+
pointerEvents: 'auto',
22+
},
23+
},
24+
},
25+
},
26+
};
27+
28+
const theme = extendTheme({components});
29+
30+
return (
31+
<ChakraProvider theme={theme}>
32+
<SessionProvider>
33+
<HomePage/>
34+
</SessionProvider>
35+
</ChakraProvider>
36+
)
1137
}
1238

1339
export default App

services/frontend/react_app/src/components/common/locationSelect/locationSelect.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const LocationSelect = ({ placeholder, locations, selectedLocationID, onS
2424
>
2525
{ locations.map(loc => {
2626
return (
27-
<option value={loc.ID}>{loc.Name}</option>
27+
<option value={loc.id}>{loc.name}</option>
2828
)
2929
})}
3030
</Select>

services/frontend/react_app/src/components/features/logs/logs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ const BaseLog = ({ log }: BaseLogProps) => {
7474
<h2>
7575
<AccordionButton>
7676
<Box as="span" flex='1' textAlign='left'>
77-
Request ID: #{requestID} from <LocationHighlight value={pickupLocation.Name} type='pickup'/> to <LocationHighlight value={dropoffLocation.Name} type='dropoff'/>
77+
Request ID: #{requestID} from <LocationHighlight value={pickupLocation.name} type='pickup'/> to <LocationHighlight value={dropoffLocation.name} type='dropoff'/>
7878
</Box>
7979
<AccordionIcon />
8080
</AccordionButton>
Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,108 @@
1+
import React, {useEffect, useMemo} from "react";
2+
import {MapContainer, TileLayer, useMap} from "react-leaflet";
3+
import "leaflet/dist/leaflet.css";
4+
import * as L from 'leaflet';
5+
import {LatLngTuple} from 'leaflet';
6+
import "leaflet-routing-machine";
17
import {Flex} from "@chakra-ui/react";
2-
import {Marker, Map as PigeonMap} from "pigeon-maps";
38

9+
interface RouteMapProps {
10+
start?: [number, number];
11+
end?: [number, number];
12+
center: LatLngTuple;
13+
}
14+
15+
const COORDINATES_MOCKED: Record<number, [number, number]> = {
16+
1: [37.7749, -122.4194], // My Home (San Francisco, downtown)
17+
123: [37.7764, -122.4241], // Rachel's Floral Designs (near Hayes Valley)
18+
392: [37.7723, -122.4108], // Trom Chocolatier (Mission District)
19+
567: [37.7807, -122.4081], // Amazing Coffee Roasters (SoMa)
20+
731: [37.7689, -122.4494] // Japanese Desserts (near Golden Gate Park)
21+
};
22+
23+
const getCenter = (start: [number, number] | undefined, end: [number, number] | undefined): LatLngTuple => {
24+
if (!start || !end) {
25+
return [37.562304, -122.32668]
26+
}
27+
28+
return [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2];
29+
}
30+
31+
const RoutingControl = ({ start, end, center }: RouteMapProps) => {
32+
const map = useMap();
33+
34+
useEffect(() => {
35+
if (!map || !start || !end) return () => {};
36+
37+
map.setView(center);
38+
39+
const customPlan = L.Routing.plan([L.latLng(start), L.latLng(end)], {
40+
createMarker: (i, waypoint) => {
41+
return L.marker(waypoint.latLng, {
42+
icon: L.icon({
43+
iconUrl: i === 0
44+
? "https://cdn-icons-png.flaticon.com/512/2991/2991122.png" // Start marker
45+
: "https://cdn-icons-png.flaticon.com/512/190/190411.png", // End marker
46+
iconSize: [25, 41],
47+
iconAnchor: [12, 41],
48+
}),
49+
});
50+
}, // Prevent default markers
51+
});
52+
53+
const routingControl = L.Routing.control({
54+
waypoints: [L.latLng(start[0], start[1]), L.latLng(end[0], end[1])],
55+
routeWhileDragging: true,
56+
addWaypoints: false,
57+
lineOptions: {
58+
styles: [{ color: "blue", weight: 6 }], // Wider path
59+
extendToWaypoints: true, // Default is true, ensures lines extend to waypoints
60+
missingRouteTolerance: 10, // Default is 10 (meters)
61+
},
62+
plan:customPlan,
63+
}).addTo(map);
64+
65+
return () => {
66+
map.removeControl(routingControl);
67+
};
68+
}, [map, start, end, center]);
69+
70+
return null;
71+
};
72+
73+
const RouteMap: React.FC<Omit<RouteMapProps, "center">> = ({start, end}) => {
74+
const center = useMemo((): LatLngTuple => {
75+
return getCenter(start, end);
76+
}, [start, end]);
77+
78+
return (
79+
<MapContainer
80+
center={center}
81+
zoom={17}
82+
style={{height: "100%", width: "100%"}}
83+
>
84+
<TileLayer
85+
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
86+
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
87+
/>
88+
<RoutingControl start={start} end={end} center={center}/>
89+
</MapContainer>
90+
);
91+
};
92+
93+
94+
type MapProps = {
95+
dropoffLocationID: number;
96+
pickupLocationID: number;
97+
}
98+
99+
export const Map = ({dropoffLocationID, pickupLocationID}: MapProps) => {
100+
const dropoffCoords = COORDINATES_MOCKED[dropoffLocationID as keyof typeof COORDINATES_MOCKED];
101+
const pickupCoords = COORDINATES_MOCKED[pickupLocationID as keyof typeof COORDINATES_MOCKED];
4102

5-
export const Map = () => {
6103
return (
7104
<Flex w='100%' h='100%' borderRadius={16} overflow='hidden'>
8-
<PigeonMap defaultCenter={[37.562304, -122.32668]} defaultZoom={17}>
9-
<Marker width={50} anchor={[37.562304, -122.32668]} />
10-
</PigeonMap>
105+
<RouteMap start={pickupCoords} end={dropoffCoords} />
11106
</Flex>
12-
)
107+
);
13108
}

services/frontend/react_app/src/pages/home.tsx

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
import {MainLayout} from "../components/layouts";
2-
import {Box, Button, Card, CardBody, CardHeader, Heading, HStack, Stack, StackDivider,} from "@chakra-ui/react";
2+
import {
3+
Box,
4+
Button,
5+
Card,
6+
CardBody,
7+
CardHeader,
8+
Drawer,
9+
DrawerBody,
10+
DrawerContent,
11+
DrawerFooter,
12+
DrawerHeader,
13+
Heading,
14+
HStack,
15+
Stack,
16+
StackDivider,
17+
useDisclosure,
18+
useToast,
19+
} from "@chakra-ui/react";
320

421
import {Logs} from "../components/features/logs/logs.tsx";
522
import {Map} from "../components/features/map/map.tsx";
@@ -19,6 +36,9 @@ export const HomePage = () => {
1936
const [selectedLocations, setSelectedLocations] = useState({pickupId: -1, dropoffId: -1});
2037
const {logs, addNewLog, addErrorEntry, addInformationEntry} = useLogs();
2138

39+
const logsModal = useDisclosure();
40+
const toast = useToast();
41+
2242
useEffect(() => {
2343
const fetchLocations = async () => {
2444
const locations = await apiGet<Locations>('/splash');
@@ -75,15 +95,26 @@ export const HomePage = () => {
7595
dropoffLocationID: dropoffId
7696
}
7797

78-
const pickupLocation = locations?.Locations.find(l => l.ID === pickupId);
79-
const dropoffLocation = locations?.Locations.find(l => l.ID === dropoffId);
98+
const pickupLocation = locations?.Locations.find(l => l.id === pickupId);
99+
const dropoffLocation = locations?.Locations.find(l => l.id === dropoffId);
100+
101+
toast({
102+
title: 'Hotrod::Drive requested.',
103+
description: `Drive requested to ${dropoffLocation?.name}`,
104+
status: 'success',
105+
duration: 9000,
106+
isClosable: true,
107+
position: "top"
108+
});
109+
110+
// Reset locations
111+
setSelectedLocations({pickupId: -1, dropoffId: -1});
80112

81113
addNewLog(pickupLocation!, dropoffLocation!, requestID, {
82114
messageType: "info",
83115
service: 'browser',
84116
date: new Date(),
85117
status: 'Requesting a ride.'
86-
87118
});
88119

89120
try {
@@ -155,14 +186,34 @@ export const HomePage = () => {
155186
</Card>
156187
</Stack>
157188
<Stack flexGrow={1} justifyContent='space-between' w='50%' h='100%' maxH={'900px'}>
158-
<Stack w='100%' backgroundColor='white' overflowY='auto'
159-
maxH={'50%'}>
160-
<Logs logs={logs}/>
161-
</Stack>
162-
189+
<Drawer
190+
isOpen={logsModal.isOpen}
191+
placement='right'
192+
onClose={() => {
193+
}}
194+
size="lg"
195+
trapFocus={false}
196+
blockScrollOnMount={false}
197+
variant="aside"
198+
>
199+
<DrawerContent>
200+
<DrawerHeader>Notifications logs</DrawerHeader>
201+
202+
<DrawerBody>
203+
<Logs logs={logs}/>
204+
</DrawerBody>
205+
206+
<DrawerFooter>
207+
<Button variant='outline' mr={3} onClick={logsModal.onClose}>
208+
Close
209+
</Button>
210+
</DrawerFooter>
211+
</DrawerContent>
212+
</Drawer>
163213
<Stack flexGrow={1} flexShrink={1}>
164-
<Map/>
214+
<Map dropoffLocationID={selectedLocations.dropoffId} pickupLocationID={selectedLocations.pickupId}/>
165215
</Stack>
216+
<Button variant='outline' size="sm" onClick={logsModal.onToggle}>Show Logs</Button>
166217
</Stack>
167218
</HStack>
168219
</MainLayout>

services/frontend/react_app/src/types/location.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export type Locations = {
66
}
77

88
export type Location = {
9-
ID: number,
10-
Name: string,
11-
Coordinates: string,
9+
id: number,
10+
name: string,
11+
coordinates: string,
1212
}

services/frontend/react_app/yarn.lock

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,16 @@
13431343
"@jridgewell/resolve-uri" "^3.1.0"
13441344
"@jridgewell/sourcemap-codec" "^1.4.14"
13451345

1346+
"@mapbox/corslite@0.0.7":
1347+
version "0.0.7"
1348+
resolved "https://registry.yarnpkg.com/@mapbox/corslite/-/corslite-0.0.7.tgz#29f5b6a188ba946e514bdf0b6401ed4fbe13a39e"
1349+
integrity sha512-w/uS474VFjmqQ7fFWIMZINQM1BAQxDLuoJaZZIPES1BmeYpCtlh9MtbFxKGGDAsfvut8/HircIsVvEYRjQ+iMg==
1350+
1351+
"@mapbox/polyline@^0.2.0":
1352+
version "0.2.0"
1353+
resolved "https://registry.yarnpkg.com/@mapbox/polyline/-/polyline-0.2.0.tgz#6e25980744aa22331f94b645a542c02d3fcfee97"
1354+
integrity sha512-GCddO0iw6AzOQqZgBmjEQI9Pgo40/yRgkTkikGctE01kNBN0ThWYuAnTD+hRWrAWMV6QJ0rNm4m8DAsaAXE7Pg==
1355+
13461356
"@nodelib/fs.scandir@2.1.5":
13471357
version "2.1.5"
13481358
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -1522,10 +1532,17 @@
15221532
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
15231533
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
15241534

1525-
"@types/leaflet@^1.9.8":
1526-
version "1.9.8"
1527-
resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.9.8.tgz#32162a8eaf305c63267e99470b9603b5883e63e8"
1528-
integrity sha512-EXdsL4EhoUtGm2GC2ZYtXn+Fzc6pluVgagvo2VC1RHWToLGlTRwVYoDpqS/7QXa01rmDyBjJk3Catpf60VMkwg==
1535+
"@types/leaflet-routing-machine@^3.2.8":
1536+
version "3.2.8"
1537+
resolved "https://registry.yarnpkg.com/@types/leaflet-routing-machine/-/leaflet-routing-machine-3.2.8.tgz#e8d3554fe0995c8ab20c32b27c778cd3bd228b55"
1538+
integrity sha512-v2pJDv/nqbB769SsytHemhLkqwjVor9UdWvZ1l6Y2SEaXNt1yDwVrktc4sCT8/4n7npuEb8VP+UAk8xrPePqSQ==
1539+
dependencies:
1540+
"@types/leaflet" "*"
1541+
1542+
"@types/leaflet@*", "@types/leaflet@^1.9.15":
1543+
version "1.9.15"
1544+
resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.9.15.tgz#5054d3cfe115da79bffb7a3a70acc6b310bd7c60"
1545+
integrity sha512-7UuggAuAs+mva66gtf2OTB1nEhzU/9JED93TIaOEgvFMvG/dIGQaukHE7izHo1Zd+Ko1L4ETUw7TBc8yUxevpg==
15291546
dependencies:
15301547
"@types/geojson" "*"
15311548

@@ -2437,6 +2454,15 @@ keyv@^4.5.3:
24372454
dependencies:
24382455
json-buffer "3.0.1"
24392456

2457+
leaflet-routing-machine@^3.2.12:
2458+
version "3.2.12"
2459+
resolved "https://registry.yarnpkg.com/leaflet-routing-machine/-/leaflet-routing-machine-3.2.12.tgz#9e4aef008321b0227cf894d829c3b4c1f13e4e13"
2460+
integrity sha512-HLde58G1YtD9xSIzZavJ6BPABZaV1hHeGst8ouhzuxmSC3s32NVtADT+njbIUMW1maHRCrsgTk/E4hz5QH7FrA==
2461+
dependencies:
2462+
"@mapbox/corslite" "0.0.7"
2463+
"@mapbox/polyline" "^0.2.0"
2464+
osrm-text-instructions "^0.13.2"
2465+
24402466
leaflet@^1.9.4:
24412467
version "1.9.4"
24422468
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d"
@@ -2564,6 +2590,11 @@ optionator@^0.9.3:
25642590
prelude-ls "^1.2.1"
25652591
type-check "^0.4.0"
25662592

2593+
osrm-text-instructions@^0.13.2:
2594+
version "0.13.4"
2595+
resolved "https://registry.yarnpkg.com/osrm-text-instructions/-/osrm-text-instructions-0.13.4.tgz#78bedabd84cbcabce9c9fd0fbb6b0fd9f06c7f9f"
2596+
integrity sha512-ge4ZTIetMQKAHKq2MwWf83ntzdJN20ndRKRaVNoZ3SkDkBNO99Qddz7r6+hrVx38I+ih6Rk5T1yslczAB6Q9Pg==
2597+
25672598
p-limit@^3.0.2:
25682599
version "3.1.0"
25692600
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"

0 commit comments

Comments
 (0)