-
+
)}
diff --git a/app/ui/src/app/results/components/Map.tsx b/app/ui/src/app/results/components/Map.tsx
index 6d03ad37..0bb99cea 100644
--- a/app/ui/src/app/results/components/Map.tsx
+++ b/app/ui/src/app/results/components/Map.tsx
@@ -20,6 +20,37 @@ import type { PendingPinMove, Route } from "../types";
import { routeColorHex } from "../utils/routeColors";
const DAVIS_CENTER = { lat: 38.5449, lng: -121.7405 };
+const MARKER_ICON_WIDTH = 28;
+const MARKER_ICON_HEIGHT = 40;
+
+function getMarkerScaledSize(): google.maps.Size | undefined {
+ if (typeof google === "undefined") return undefined;
+ return new google.maps.Size(MARKER_ICON_WIDTH, MARKER_ICON_HEIGHT);
+}
+
+// fillColor is always a route palette hex from routeColorHex, never user input.
+function markerSvgDataUrl(fillColor: string): string {
+ const svg = `
`;
+ return `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(svg)}`;
+}
+
+function createRoutePinElement(fillColor: string): HTMLElement {
+ const wrapper = document.createElement("div");
+ wrapper.style.width = `${MARKER_ICON_WIDTH}px`;
+ wrapper.style.height = `${MARKER_ICON_HEIGHT}px`;
+ // Anchor bottom-center of the pin on the stop (tip is at y=40 in the SVG).
+ wrapper.style.transform = "translate(-50%, -100%)";
+
+ const img = document.createElement("img");
+ img.src = markerSvgDataUrl(fillColor);
+ img.width = MARKER_ICON_WIDTH;
+ img.height = MARKER_ICON_HEIGHT;
+ img.style.display = "block";
+ img.draggable = false;
+ img.alt = "";
+ wrapper.appendChild(img);
+ return wrapper;
+}
function routePolylineOptions(
strokeColor: string,
@@ -307,7 +338,8 @@ function AdvancedMarkers({
if (cancelled) return;
- routes.forEach((route) => {
+ routes.forEach((route, routeIndex) => {
+ const accentColor = routeColorHex(routeIndex);
const sorted = [...route.stops].sort(
(a, b) => a.sequence - b.sequence,
);
@@ -319,6 +351,7 @@ function AdvancedMarkers({
position,
title: stop.address,
gmpDraggable: isEditMode,
+ content: createRoutePinElement(accentColor),
});
m.addListener("dragend", () => {
@@ -466,7 +499,10 @@ export default function MapComponent({
/>
)}
{!mapId &&
- routes.map((route) => {
+ routes.map((route, routeIndex) => {
+ const accentColor = routeColorHex(routeIndex);
+ const iconUrl = markerSvgDataUrl(accentColor);
+ const scaledSize = getMarkerScaledSize();
const sorted = [...route.stops].sort(
(a, b) => a.sequence - b.sequence,
);
@@ -485,6 +521,18 @@ export default function MapComponent({
key={stop.id}
position={position}
title={stop.address}
+ icon={
+ scaledSize
+ ? {
+ url: iconUrl,
+ scaledSize,
+ anchor: new google.maps.Point(
+ MARKER_ICON_WIDTH / 2,
+ MARKER_ICON_HEIGHT,
+ ),
+ }
+ : { url: iconUrl }
+ }
draggable={isEditMode}
onDragEnd={(e) => {
const latLng = e.latLng;
diff --git a/app/ui/src/app/results/components/Sidebar.tsx b/app/ui/src/app/results/components/Sidebar.tsx
index 76a6bed9..008256f5 100644
--- a/app/ui/src/app/results/components/Sidebar.tsx
+++ b/app/ui/src/app/results/components/Sidebar.tsx
@@ -1,5 +1,6 @@
// Sidebar: route cards, expand/collapse stops, edit-mode toggle.
+import Image from "next/image";
import { useMemo, useState } from "react";
import type { Route } from "../types";
import { routeColorHex } from "../utils/routeColors";
@@ -45,154 +46,263 @@ export default function Sidebar({
}
return (
-