Skip to content

Commit c538d2e

Browse files
committed
Merge remote-tracking branch 'origin/632-view-on-map-button' into search-map-button
2 parents 188924d + ac86f7d commit c538d2e

5 files changed

Lines changed: 933 additions & 505 deletions

File tree

backend/src/dbInterface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ type BuildingsAndRoomsRes = {
8585
school: string;
8686
usage: string;
8787
capacity: number;
88+
lat: number;
89+
long: number;
8890
}>;
8991
}>;
9092
};
@@ -109,6 +111,8 @@ export const queryBuildingsAndRooms =
109111
school
110112
usage
111113
capacity
114+
lat
115+
long
112116
}
113117
}
114118
}

common/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export type Room = {
1717
capacity: number;
1818
usage: string;
1919
school: string;
20+
lat: number;
21+
long: number;
2022
};
2123

2224
export type Booking = {

frontend/components/Map.tsx

Lines changed: 12 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,11 @@
22
import "mapbox-gl/dist/mapbox-gl.css";
33

44
import { Building } from "@common/types";
5-
import mbxDirections from "@mapbox/mapbox-sdk/services/directions";
65
import Box from "@mui/material/Box";
76
import { DarkModeContext } from "app/clientLayout";
8-
import { useSearchParams } from "next/navigation";
97
import React, { useContext, useEffect, useRef, useState } from "react";
10-
import {
11-
Layer,
12-
LngLatBoundsLike,
13-
Map,
14-
MapRef,
15-
Marker,
16-
Source,
17-
} from "react-map-gl/mapbox";
8+
import { LngLatBoundsLike, Map, MapRef, Marker } from "react-map-gl/mapbox";
189
import { useDebounceValue } from "usehooks-ts";
19-
import BuildingDrawer from "views/BuildingDrawer";
2010

2111
import { MAPBOX_ACCESS_TOKEN } from "../config";
2212
import useBuildings from "../hooks/useBuildings";
@@ -66,25 +56,6 @@ if (!MAPBOX_ACCESS_TOKEN) {
6656
throw new Error("Missing Mapbox access token");
6757
}
6858

69-
const directionsClient = mbxDirections({ accessToken: MAPBOX_ACCESS_TOKEN });
70-
71-
const fetchRoute = async (start: [number, number], end: [number, number]) => {
72-
try {
73-
const response = await directionsClient
74-
.getDirections({
75-
profile: "walking", // or 'driving', 'cycling'
76-
geometries: "geojson",
77-
waypoints: [{ coordinates: start }, { coordinates: end }],
78-
})
79-
.send();
80-
81-
return response.body.routes[0].geometry;
82-
} catch (error: any) {
83-
console.error("Route fetch error:", error.response?.body || error.message);
84-
throw error;
85-
}
86-
};
87-
8859
export const MapComponent = () => {
8960
const { buildings } = useBuildings();
9061
const { isDarkMode } = useContext(DarkModeContext);
@@ -100,20 +71,12 @@ export const MapComponent = () => {
10071
setRoomIdToFocus(params.get("roomId") ?? undefined);
10172
}, []);
10273

103-
// useEffect(() => {
104-
// // Run only on client side, no Suspense issues
105-
// const params = new URLSearchParams(window.location.search);
106-
// setRoomIdToFocus(params.get("roomId") ?? undefined);
107-
// }, []);
108-
// const roomIdToFocus = useSearchParams().get("roomId") ?? undefined;
10974
const mapRef = useRef<MapRef>(null);
11075

11176
// Use debounce to allow moving from marker to popup without popup hiding
11277
const [currentHover, setCurrentHover] = useState<Building | null>(null);
11378
const [debouncedCurrentHover] = useDebounceValue(currentHover, 50);
11479

115-
const [routeGeoJSON, setRouteGeoJSON] = useState<any | null>(null);
116-
11780
const style = isDarkMode
11881
? "mapbox://styles/bengodw/cmcimql2101qo01sp7dricgzq"
11982
: "mapbox://styles/bengodw/cmcimp1tz002p01rcfzbd8btn";
@@ -130,21 +93,6 @@ export const MapComponent = () => {
13093
}
13194
}, [buildings, userLat, userLng]);
13295

133-
const handleMarkerClick = async (building: Building) => {
134-
if (!userLat || !userLng) return;
135-
136-
const geometry = await fetchRoute(
137-
[userLng, userLat],
138-
[building.long, building.lat]
139-
);
140-
141-
setRouteGeoJSON({
142-
type: "Feature",
143-
properties: {},
144-
geometry,
145-
});
146-
};
147-
14896
return (
14997
<div style={{ height: "100%", position: "relative" }}>
15098
<Map
@@ -160,7 +108,6 @@ export const MapComponent = () => {
160108
key={building.id}
161109
latitude={building.lat}
162110
longitude={building.long}
163-
onClick={() => handleMarkerClick(building)}
164111
>
165112
<MapMarker
166113
buildingId={building.id}
@@ -177,34 +124,17 @@ export const MapComponent = () => {
177124
</Marker>
178125
)}
179126

180-
<RoomMapMarker
181-
roomId={roomIdToFocus}
182-
roomLocation={(lat, long) => {
183-
mapRef.current?.flyTo({
184-
center: [long, lat],
185-
zoom: 19.5,
186-
duration: 1000,
187-
});
188-
}}
189-
/>
190-
191-
{routeGeoJSON && (
192-
<>
193-
<Source id="route" type="geojson" data={routeGeoJSON} />
194-
<Layer
195-
id="route-line"
196-
type="line"
197-
source="route"
198-
layout={{
199-
"line-cap": "round",
200-
"line-join": "round",
201-
}}
202-
paint={{
203-
"line-color": "#1DB954",
204-
"line-width": 4,
205-
}}
206-
/>
207-
</>
127+
{roomIdToFocus && (
128+
<RoomMapMarker
129+
roomId={roomIdToFocus}
130+
roomLocation={(lat, long) => {
131+
mapRef.current?.flyTo({
132+
center: [long, lat],
133+
zoom: 19.5,
134+
duration: 1000,
135+
});
136+
}}
137+
/>
208138
)}
209139
</Map>
210140
</div>

frontend/components/RoomMapMarker.tsx

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,77 @@
11
"use client";
2-
import Image from "next/image";
3-
import { useEffect, useState } from "react";
4-
import { Marker } from "react-map-gl/mapbox";
52

6-
type RoomMarkersData = {
7-
[roomId: string]: [number, number];
8-
};
3+
import { alpha } from "@mui/material";
4+
import { Typography } from "@mui/material";
5+
import Box from "@mui/material/Box";
6+
import { useTheme } from "@mui/material/styles";
7+
import useRoom from "hooks/useRoom";
8+
import React, { useEffect, useRef } from "react";
9+
import { Marker } from "react-map-gl/mapbox";
910

10-
type RoomMarker = {
11-
id: string;
12-
lat: number;
13-
long: number;
14-
};
11+
interface RoomMapMarkerProps {
12+
roomId: string;
13+
roomLocation?: (lat: number, long: number) => void;
14+
}
1515

16-
const RoomMapMarker = ({
16+
const RoomMapMarker: React.FC<RoomMapMarkerProps> = ({
1717
roomId,
1818
roomLocation,
19-
}: {
20-
roomId?: string;
21-
roomLocation?: (lat: number, long: number) => void;
2219
}) => {
23-
// Changing data structure to {[id: string, lat: number, long: number]...}
24-
const [roomMarker, setRoomMarker] = useState<RoomMarker | null>(null);
20+
const { room } = useRoom(roomId);
21+
const hasFocusedRef = useRef(false);
22+
const theme = useTheme();
23+
2524
useEffect(() => {
26-
if (!roomId) return;
27-
fetch("/roommarkers.json")
28-
.then((res) => res.json())
29-
.then((markersData) => {
30-
const data = markersData as RoomMarkersData;
31-
const roomCoords = data[roomId];
32-
33-
if (roomCoords) {
34-
const [longitude, latitude] = roomCoords;
35-
const marker: RoomMarker = {
36-
id: roomId,
37-
lat: latitude,
38-
long: longitude,
39-
};
40-
setRoomMarker(marker);
41-
42-
if (roomLocation) {
43-
roomLocation(latitude, longitude);
44-
}
45-
}
46-
})
47-
.catch((err) => console.error("Failed to load room markers:", err));
48-
}, [roomId, roomLocation]);
49-
50-
if (!roomMarker) return null;
25+
if (!hasFocusedRef.current && room && roomLocation) {
26+
roomLocation(room.lat, room.long);
27+
hasFocusedRef.current = true;
28+
}
29+
}, [room, roomLocation]);
30+
31+
if (!room) return null;
32+
const blue = "#1976d2";
33+
5134
return (
52-
<Marker
53-
key={roomMarker.id}
54-
latitude={roomMarker.lat}
55-
longitude={roomMarker.long}
56-
anchor="bottom"
57-
>
58-
<Image src="/MapPin.png" alt="Room pin" width={30} height={30} />
35+
<Marker latitude={room.lat} longitude={room.long} anchor="bottom">
36+
<div
37+
style={{
38+
display: "flex",
39+
flexDirection: "column",
40+
alignItems: "center",
41+
position: "relative",
42+
}}
43+
>
44+
<Typography
45+
sx={{
46+
fontSize: 11,
47+
fontWeight: 500,
48+
textShadow:
49+
theme.palette.mode === "light"
50+
? "-.5px -.5px 1px #f2f2f2, .5px -.5px 1px #f2f2f2, -.5px .5px 1px #f2f2f2, .5px .5px 1px #f2f2f2"
51+
: "",
52+
color: theme.palette.text.primary,
53+
marginBottom: 1,
54+
userSelect: "none",
55+
}}
56+
>
57+
{room.name}
58+
</Typography>
59+
60+
<Box
61+
sx={(theme) => ({
62+
width: 18,
63+
height: 18,
64+
borderRadius: "50%",
65+
border: `5px solid ${blue}`,
66+
backgroundColor: "white",
67+
boxShadow: `0px 0px 6px 4px ${alpha(blue, 0.5)}`,
68+
position: "relative",
69+
"&:hover": {
70+
cursor: "pointer",
71+
},
72+
})}
73+
/>
74+
</div>
5975
</Marker>
6076
);
6177
};

0 commit comments

Comments
 (0)