diff --git a/client/src/components/Map/PlannerWaypointMarker.tsx b/client/src/components/Map/PlannerWaypointMarker.tsx index d620c583e..0af95908a 100644 --- a/client/src/components/Map/PlannerWaypointMarker.tsx +++ b/client/src/components/Map/PlannerWaypointMarker.tsx @@ -2,7 +2,7 @@ import L from 'leaflet'; import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { Marker, Tooltip, useMap } from 'react-leaflet'; import { MAP_LABEL_BADGE_CLASS } from './mapHoverLabel'; -import { plannerPinShape, type PlannerPinPoint } from './plannerPinShape'; +import { plannerPinBadge, plannerPinShape, type PlannerPinPoint } from './plannerPinShape'; import { wirePlannerWaypointDrag } from './plannerWaypointDrag'; /** The pin's inline style for a size and selection; the renderer owns the colour. */ @@ -20,7 +20,7 @@ function plannerWaypointIcon( className: 'tour-planner-waypoint-marker', iconSize: [size, size], iconAnchor: [size / 2, size / 2], - html: `${text}`, + html: `${text}${plannerPinBadge(point)}`, }); } @@ -46,10 +46,10 @@ export default function PlannerWaypointMarker({ handlersRef.current = { onClick, onMove }; }, [onClick, onMove]); const position = useMemo(() => [point.lat, point.lng] as [number, number], [point.lat, point.lng]); - const { label, shaping } = point; + const { label, shaping, stageEnd } = point; const icon = useMemo( - () => plannerWaypointIcon(pinCss, { label, shaping }, index, selected, !!onMove), - [index, label, onMove, pinCss, selected, shaping] + () => plannerWaypointIcon(pinCss, { label, shaping, stageEnd }, index, selected, !!onMove), + [index, label, onMove, pinCss, selected, shaping, stageEnd] ); const eventHandlers = useMemo( () => ({ diff --git a/client/src/components/Map/mapViewContract.ts b/client/src/components/Map/mapViewContract.ts index f8ebe9e1a..ebe0cc691 100644 --- a/client/src/components/Map/mapViewContract.ts +++ b/client/src/components/Map/mapViewContract.ts @@ -118,6 +118,8 @@ export interface MapViewProps { detail?: string; label?: string; shaping?: boolean; + /** MDT-01: a stage of a multi-day tour ends here; the pin carries a moon at its corner. */ + stageEnd?: boolean; }>; selectedPlannerWaypointId?: string | null; onPlannerWaypointClick?: (id: string) => void; diff --git a/client/src/components/Map/plannerPinShape.test.ts b/client/src/components/Map/plannerPinShape.test.ts index 8e15b4764..ea1d2c654 100644 --- a/client/src/components/Map/plannerPinShape.test.ts +++ b/client/src/components/Map/plannerPinShape.test.ts @@ -1,6 +1,7 @@ // FE-PLANNER-PIN-001: a stop is a numbered pin, a shaping point a small dot without a number (P2A-03). +// FE-PLANNER-PIN-002: the stop where a stage of a multi-day tour ends carries the moon at its corner (MDT-01). import { describe, expect, it } from 'vitest'; -import { plannerPinShape } from './plannerPinShape'; +import { plannerPinBadge, plannerPinShape } from './plannerPinShape'; describe('plannerPinShape', () => { it('FE-PLANNER-PIN-001: numbers a stop by its label or its place, and leaves a shaping point blank and small', () => { @@ -9,4 +10,13 @@ describe('plannerPinShape', () => { expect(plannerPinShape({ shaping: true, label: '9' }, 1, false)).toEqual({ size: 14, text: '' }); expect(plannerPinShape({ shaping: true }, 1, true)).toEqual({ size: 18, text: '' }); }); + + it('FE-PLANNER-PIN-002: marks a stage end with the moon at the corner, and nothing else', () => { + const badge = plannerPinBadge({ stageEnd: true, label: '3' }); + expect(badge).toContain('data-stage-end="true"'); + expect(badge).toContain('