diff --git a/client/src/components/Collections/CollectionMap.test.tsx b/client/src/components/Collections/CollectionMap.test.tsx index bfc230529..044dcb84a 100644 --- a/client/src/components/Collections/CollectionMap.test.tsx +++ b/client/src/components/Collections/CollectionMap.test.tsx @@ -12,6 +12,7 @@ import L from 'leaflet'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import { act, render, waitFor } from '../../../tests/helpers/render'; import { resetAllStores } from '../../../tests/helpers/store'; +import { withMarkerCategory } from '../../pages/collections/collectionsModel'; import CollectionMap from './CollectionMap'; vi.mock('../../hooks/useGeolocation', () => ({ @@ -258,3 +259,26 @@ describe('CollectionMap framing', () => { expect(wrappedIn(map, '[--bottom-nav-h:0px]')).toBe(true); }); }); + +describe('CollectionMap pins (#3073)', () => { + const MUSEUM = { id: 4, name: 'Museum', color: '#7c3aed', icon: 'Landmark' }; + + it('FE-COMP-COLMAP-008: a saved place carries its category where the markers read it', () => { + const pin = withMarkerCategory({ ...place(1, 'Belvedere', 48.1915, 16.3809), category_id: 4, category: MUSEUM }); + expect(pin).toMatchObject({ category_name: 'Museum', category_color: '#7c3aed', category_icon: 'Landmark' }); + expect(withMarkerCategory(place(2, 'Somewhere', 48.2, 16.37))).toMatchObject({ + category_name: null, + category_color: null, + category_icon: null, + }); + }); + + it('FE-COMP-COLMAP-009: the pin of a place with a category wears its colour, not the grey default', async () => { + const { view } = await renderMap([ + { ...place(1, 'Belvedere', 48.1915, 16.3809), category_id: 4, category: MUSEUM }, + ]); + await waitFor(() => expect(view.container.querySelector('.leaflet-marker-icon')).not.toBeNull()); + const html = view.container.querySelector('.leaflet-marker-icon')?.innerHTML ?? ''; + expect(html.toLowerCase()).toContain('#7c3aed'); + }); +}); diff --git a/client/src/components/Collections/CollectionMap.tsx b/client/src/components/Collections/CollectionMap.tsx index ad9ece60f..7a5d6e201 100644 --- a/client/src/components/Collections/CollectionMap.tsx +++ b/client/src/components/Collections/CollectionMap.tsx @@ -1,7 +1,7 @@ import React, { useMemo } from 'react' import { MapViewAuto } from '../Map/MapViewAuto' import type { CollectionPlace } from '@trek/shared' -import { mappablePlaces } from '../../pages/collections/collectionsModel' +import { mappablePlaces, withMarkerCategory } from '../../pages/collections/collectionsModel' import { useTileUrl } from '../../hooks/useTileUrl' import { OFM_DARK, OFM_POSITRON } from '../../constants/mapDefaults' import type { ViewportPadding } from '../../utils/mapViewport' @@ -70,7 +70,7 @@ function framePoints(places: CollectionPlace[]): [number, number][] { * while the map is built rather than flying in. */ export default function CollectionMap({ places, selectedPlaceId, onOpenPlace, onDeselect, dark, underBar = false }: CollectionMapProps): React.ReactElement { - const pts = useMemo(() => mappablePlaces(places), [places]) + const pts = useMemo(() => mappablePlaces(places).map(withMarkerCategory), [places]) const tileUrl = useTileUrl(dark ? OFM_DARK : OFM_POSITRON) const focusPoints = useMemo(() => framePoints(pts), [pts]) // The places as a set: another list, a filter or a new place frames again, while a diff --git a/client/src/pages/collections/collectionsModel.ts b/client/src/pages/collections/collectionsModel.ts index 454f3881b..f306b46dc 100644 --- a/client/src/pages/collections/collectionsModel.ts +++ b/client/src/pages/collections/collectionsModel.ts @@ -93,6 +93,25 @@ export function mappablePlaces(places: CollectionPlace[]): CollectionPlace[] { return places.filter(p => p && typeof p.lat === 'number' && typeof p.lng === 'number') } +/** + * A saved place with its category where the trip map reads it. The server nests a + * collection place's category (`category.color`), the markers and the hover card + * read the flat fields a trip place carries, so every pin here was the grey default + * (#3073). + */ +export function withMarkerCategory(place: CollectionPlace): CollectionPlace & { + category_name: string | null + category_color: string | null + category_icon: string | null +} { + return { + ...place, + category_name: place.category?.name ?? null, + category_color: place.category?.color ?? null, + category_icon: place.category?.icon ?? null, + } +} + /** * Normalise a user-typed link: prepend https:// when there's no scheme so the * href is absolute (a bare "booking.com" would otherwise resolve relative to the