Skip to content

Added mapbox support to all pages - #74

Closed
raptor1820 wants to merge 1 commit into
mainfrom
ritwic/mapbox
Closed

raptor1820 wants to merge 1 commit into
mainfrom
ritwic/mapbox

Conversation

@raptor1820

Copy link
Copy Markdown
Collaborator

Made a mapbox component and re-used it across pages.

@netlify

netlify Bot commented Apr 12, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for able-alliance ready!

Name Link
🔨 Latest commit e1186be
🔍 Latest deploy log https://app.netlify.com/projects/able-alliance/deploys/69db200ce271b90008b93356
😎 Deploy Preview https://deploy-preview-74--able-alliance.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@raptor1820 raptor1820 linked an issue Apr 12, 2026 that may be closed by this pull request
@greptile-apps

greptile-apps Bot commented Apr 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extracts inline Mapbox logic from rides/[id]/page.tsx and rides/new/page.tsx into a reusable RideMap component, eliminating ~250 lines of duplicated map setup code. The seed script is also improved with a computeEstimatedDropoffTime helper, backfill logic for existing routes, and additional test rides for the mock student account.

  • P1 – Marker flicker on GPS updates: The single marker effect in RideMap handles static and live markers together. Both Object.values(locations) (in rides/[id]/page.tsx) and the inline rideMapLiveMarkers array create new references on every parent render; because both are in the effect's dependency array, every GPS position update tears down and recreates all markers — including static pickup/dropoff pins.
  • P2 – Hardcoded hex colors: #2563eb and #183777 appear in RideMap.module.css and RideMap.tsx directly instead of using global CSS variables.
  • P2 – px units in box-shadow: Several box-shadow values use px offsets; project convention is rem (1rem = 10px).

Confidence Score: 4/5

Safe to merge after addressing the marker-rebuild P1; P2 style issues can follow in a cleanup PR.

One P1 issue remains: static pickup/dropoff markers are rebuilt on every live GPS update due to unstable prop references, causing visible flickering during active rides. The P2 findings (hardcoded colors, px box-shadow, console.error) are style violations that don't block functionality.

src/components/RideMap/RideMap.tsx — split the marker effect or use stable references to prevent the per-GPS-update full rebuild.

Important Files Changed

Filename Overview
src/components/RideMap/RideMap.tsx New reusable map component — good abstraction, but the single marker effect handles both static and live markers together, causing all markers to rebuild on every GPS update due to new array references from the parent.
src/components/RideMap/RideMap.module.css New CSS module with consolidated map pin/dot styles; hardcoded hex colors (#2563eb, #183777) should use CSS variables, and box-shadow px values should be converted to rem.
src/app/rides/[id]/page.tsx Significantly simplified by delegating to RideMap; locations state correctly upgraded to Record<string, Location>; inline rideMapLiveMarkers array and Object.values(locations) passed without useMemo, contributing to the marker rebuild issue.
src/app/rides/new/page.tsx Correctly refactored to ID-based location selection and delegates map rendering to RideMap; form submission logic is sound.
scripts/seed.ts Good DRY refactor of Mapbox duration helpers; backfill logic and new test routes for student1 are clean; NEXT_PUBLIC_MAPBOX_TOKEN fallback is clearly documented.

Sequence Diagram

sequenceDiagram
    participant Parent as rides/[id]/page.tsx
    participant RM as RideMap
    participant MB as Mapbox GL

    Parent->>RM: locations (Object.values), pickupLocationId, dropoffLocationId, liveMarkers (inline array)
    RM->>MB: new mapboxgl.Map() [once on mount]
    MB-->>RM: "load" event → setMapReady(true)

    Note over Parent,RM: GPS update cycle (every ~1s)
    Parent->>Parent: setOtherPartyLocation / setSelfLocation → re-render
    Parent->>RM: NEW liveMarkers reference + NEW locations reference
    RM->>MB: remove ALL markers (static + live)
    RM->>MB: re-add pickup pin, dropoff pin
    RM->>MB: re-add live markers
    RM->>MB: fitBounds (duration: 0)
Loading

Reviews (1): Last reviewed commit: "Added mapbox support" | Re-trigger Greptile

Comment on lines +193 to +338
useEffect(() => {
const map = mapRef.current;
if (!map || !mapReady) return;

markerRefs.current.forEach((marker) => marker.remove());
markerRefs.current = [];

const framePoints: [number, number][] = [];
const selectedIds = new Set(
[pickupLocationId, dropoffLocationId].filter(
(value): value is string =>
typeof value === "string" && value.length > 0,
),
);

if (showOtherLocations) {
for (const location of locations) {
if (selectedIds.has(location._id)) continue;
if (!isValidLngLat(location.longitude, location.latitude)) continue;

const marker = new mapboxgl.Marker({
element: createLocationDot(location.name),
anchor: "center",
})
.setLngLat([location.longitude, location.latitude])
.addTo(map);

markerRefs.current.push(marker);
}
}

const pickupLngLat =
selectedLocations.pickup &&
isValidLngLat(
selectedLocations.pickup.longitude,
selectedLocations.pickup.latitude,
)
? ([
selectedLocations.pickup.longitude,
selectedLocations.pickup.latitude,
] as [number, number])
: null;
const dropoffLngLat =
selectedLocations.dropoff &&
isValidLngLat(
selectedLocations.dropoff.longitude,
selectedLocations.dropoff.latitude,
)
? ([
selectedLocations.dropoff.longitude,
selectedLocations.dropoff.latitude,
] as [number, number])
: null;

const overlap =
pickupLngLat !== null &&
dropoffLngLat !== null &&
coordsOverlap(pickupLngLat, dropoffLngLat);

if (selectedLocations.pickup && pickupLngLat) {
const marker = new mapboxgl.Marker({
element: createCustomPin(
`Pickup: ${selectedLocations.pickup.name}`,
"#183777",
),
anchor: "bottom",
})
.setLngLat(pickupLngLat)
.addTo(map);

markerRefs.current.push(marker);
framePoints.push(pickupLngLat);
}

if (selectedLocations.dropoff && dropoffLngLat) {
const marker = new mapboxgl.Marker({
element: createCustomPin(
`Dropoff: ${selectedLocations.dropoff.name}`,
"#183777",
overlap ? 50 : 0,
),
anchor: "bottom",
})
.setLngLat(dropoffLngLat)
.addTo(map);

markerRefs.current.push(marker);
framePoints.push(dropoffLngLat);
}

for (const liveMarker of liveMarkers) {
if (!isValidLngLat(liveMarker.longitude, liveMarker.latitude)) continue;

const lngLat: [number, number] = [
liveMarker.longitude,
liveMarker.latitude,
];
const color = liveMarker.color ?? "#2563eb";
const variant = liveMarker.variant ?? "pin";
const marker = new mapboxgl.Marker({
element:
variant === "dot"
? createLiveDot(color)
: createCustomPin(liveMarker.label ?? "Live", color),
anchor: variant === "dot" ? "center" : "bottom",
})
.setLngLat(lngLat)
.addTo(map);

markerRefs.current.push(marker);
framePoints.push(lngLat);
}

map.resize();

if (framePoints.length === 0) {
map.easeTo({ center: defaultCenter, zoom: defaultZoom, duration: 0 });
return;
}

if (framePoints.length === 1) {
map.easeTo({ center: framePoints[0], zoom: defaultZoom, duration: 0 });
return;
}

const bounds = new mapboxgl.LngLatBounds(framePoints[0], framePoints[0]);
for (const point of framePoints.slice(1)) {
bounds.extend(point);
}

map.fitBounds(bounds, {
padding: FRAME_PADDING,
maxZoom: MAX_FRAME_ZOOM,
duration: 0,
});
}, [
defaultCenter,
defaultZoom,
dropoffLocationId,
liveMarkers,
locations,
mapReady,
pickupLocationId,
selectedLocations,
showOtherLocations,
]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 All markers rebuilt on every live GPS update

The single useEffect handles both static (pickup/dropoff) and live markers together. Both liveMarkers and locations are in the dependency array, and the caller passes Object.values(locations) (new array each render) and an inline-computed rideMapLiveMarkers array (also new each render). Every GPS position update re-renders the parent, which produces new array references for both props, triggering this effect to tear down and recreate all markers — including the static pickup/dropoff pins — every second. This causes visible flickering and wasted DOM work during live tracking.

Consider splitting static markers and live markers into separate useEffects, and using marker.setLngLat() for live updates instead of recreating markers. The caller should also useMemo the rideMapLiveMarkers array.

Comment on lines +54 to +64
background: #2563eb;
border: 0.2rem solid white;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

.mapDotTooltip {
position: absolute;
bottom: calc(100% + 0.5rem);
left: 50%;
transform: translateX(-50%);
background: #183777;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Hardcoded color values — use CSS variables

#2563eb (.mapLocationDot) and #183777 (.mapDotTooltip) are hardcoded hex values. Per project rules, color variables from the global stylesheet should be used instead of hardcoded color values in component CSS files. The same colors are also hardcoded in RideMap.tsx (lines 256, 271, 290) and should be replaced with CSS variables or Tailwind config colors.

Rule Used: Use color variables from the global stylesheet (`s... (source)

Learnt From
GTBitsOfGood/design-system#61

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

font-family: var(--font-paragraph);
line-height: 1.4;
white-space: nowrap;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 px units in box-shadow — use rem

box-shadow: 0 2px 6px ... uses px for offsets. Per the project convention (1rem = 10px), these should be 0 0.2rem 0.6rem. The same applies to .mapPinDot (line 38: 0 2px 4px) and .mapLiveDot (line 91: 0 2px 8px).

Rule Used: Use 'rem' units instead of 'px' for most CSS measu... (source)

Learnt From
GTBitsOfGood/design-system#61

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

map.on("load", handleLoad);
}
} catch (error) {
console.error("Failed to initialize ride map:", error);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 console.error in production component

The checklist rule is "no console.logs or commented-out code." console.error falls in the same category for production client components. The onError callback is already called on the next line to surface the error to the UI — the console.error can be removed.

Context Used: Fill out this checklist in addition to code commen... (source)

@chrisgoggins
chrisgoggins deleted the ritwic/mapbox branch September 1, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue 1

2 participants