From 66d85729ff41e6e536a5a255dec593ffd35878ac Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 13 Nov 2026 09:18:50 +0100 Subject: [PATCH] feat(categories): icons for parking, bus stops, stations, airports and fuel (#2706) The category picker had no icon to tell a car park or a terminal apart from the places a trip visits. Five transport icons join the shared table, which the admin pickers, map pins and the PDF all read; the ship's tooltip now also says ferry. --- .../components/shared/categoryIcons.test.ts | 29 +++++++++++++++++++ client/src/components/shared/categoryIcons.ts | 5 +++- wiki/Admin-Categories.md | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 client/src/components/shared/categoryIcons.test.ts diff --git a/client/src/components/shared/categoryIcons.test.ts b/client/src/components/shared/categoryIcons.test.ts new file mode 100644 index 000000000..37a74a076 --- /dev/null +++ b/client/src/components/shared/categoryIcons.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from 'vitest'; + +import { CATEGORY_ICON_MAP, ICON_LABELS, getCategoryIcon } from './categoryIcons'; + +import { BusFront, Fuel, MapPin, ParkingSquare, PlaneTakeoff, TrainFront } from 'lucide-react'; + +describe('categoryIcons', () => { + it('FE-COMP-CATICONS-001: every icon the category picker offers has a label for its tooltip', () => { + expect(Object.keys(CATEGORY_ICON_MAP).filter((name) => !ICON_LABELS[name])).toEqual([]); + expect(Object.keys(ICON_LABELS).filter((name) => !CATEGORY_ICON_MAP[name])).toEqual([]); + }); + + it('FE-COMP-CATICONS-002: parking, stops, stations, airports and fuel have icons of their own (#2706)', () => { + expect(getCategoryIcon('ParkingSquare')).toBe(ParkingSquare); + expect(getCategoryIcon('BusFront')).toBe(BusFront); + expect(getCategoryIcon('TrainFront')).toBe(TrainFront); + expect(getCategoryIcon('PlaneTakeoff')).toBe(PlaneTakeoff); + expect(getCategoryIcon('Fuel')).toBe(Fuel); + expect(ICON_LABELS.ParkingSquare).toBe('Parking'); + expect(ICON_LABELS.PlaneTakeoff).toBe('Airport'); + expect(ICON_LABELS.Ship).toBe('Ship / Ferry'); + }); + + it('FE-COMP-CATICONS-003: an unknown or missing icon name falls back to the pin', () => { + expect(getCategoryIcon('NoSuchIcon')).toBe(MapPin); + expect(getCategoryIcon(null)).toBe(MapPin); + expect(getCategoryIcon(undefined)).toBe(MapPin); + }); +}); diff --git a/client/src/components/shared/categoryIcons.ts b/client/src/components/shared/categoryIcons.ts index 37009b823..176f12549 100644 --- a/client/src/components/shared/categoryIcons.ts +++ b/client/src/components/shared/categoryIcons.ts @@ -1,6 +1,7 @@ import { MapPin, Building2, BedDouble, UtensilsCrossed, Landmark, ShoppingBag, Bus, Train, Car, Plane, Ship, Bike, + BusFront, TrainFront, PlaneTakeoff, ParkingSquare, Fuel, Activity, Dumbbell, Mountain, Tent, Anchor, Coffee, Beer, Wine, Utensils, Camera, Music, Theater, Ticket, @@ -15,6 +16,7 @@ import { export const CATEGORY_ICON_MAP: Record = { MapPin, Building2, BedDouble, UtensilsCrossed, Landmark, ShoppingBag, Bus, Train, Car, Plane, Ship, Bike, + BusFront, TrainFront, PlaneTakeoff, ParkingSquare, Fuel, Activity, Dumbbell, Mountain, Tent, Anchor, Coffee, Beer, Wine, Utensils, Camera, Music, Theater, Ticket, @@ -28,7 +30,8 @@ export const CATEGORY_ICON_MAP: Record = { export const ICON_LABELS: Record = { MapPin: 'Pin', Building2: 'Building', BedDouble: 'Hotel', UtensilsCrossed: 'Restaurant', Landmark: 'Attraction', ShoppingBag: 'Shopping', Bus: 'Bus', Train: 'Train', - Car: 'Car', Plane: 'Plane', Ship: 'Ship', Bike: 'Bicycle', + Car: 'Car', Plane: 'Plane', Ship: 'Ship / Ferry', Bike: 'Bicycle', + BusFront: 'Bus stop', TrainFront: 'Station', PlaneTakeoff: 'Airport', ParkingSquare: 'Parking', Fuel: 'Fuel', Activity: 'Activity', Dumbbell: 'Fitness', Mountain: 'Mountain', Tent: 'Camping', Anchor: 'Harbor', Coffee: 'Cafe', Beer: 'Bar', Wine: 'Wine', Utensils: 'Food', Camera: 'Photo', Music: 'Music', Theater: 'Theater', Ticket: 'Events', diff --git a/wiki/Admin-Categories.md b/wiki/Admin-Categories.md index 86b5593da..bde85d3e1 100644 --- a/wiki/Admin-Categories.md +++ b/wiki/Admin-Categories.md @@ -18,7 +18,7 @@ A category is a label consisting of a name, a color, and an icon. Users assign c Click **New Category** (top right of the card). A dialog opens: 1. **Category name**: required. Type it into the dialog's head; **Enter** saves. -2. **Icon**: a grid of 47 curated Lucide icons (Pin, Hotel, Restaurant, Transport, Nature and so on). Click an icon to select it. The default icon is `MapPin`. +2. **Icon**: a grid of 54 curated Lucide icons (Pin, Hotel, Restaurant, Bus stop, Station, Airport, Parking, Nature and so on). Click an icon to select it. The default icon is `MapPin`. 3. **Color**: 12 preset swatches plus **Choose custom color** (pipette button). The default color is `#6366f1` (indigo). The 12 presets are: `#6366f1`, `#8b5cf6`, `#ec4899`, `#ef4444`, `#f97316`, `#f59e0b`, `#10b981`, `#06b6d4`, `#3b82f6`, `#84cc16`, `#6b7280`, `#1f2937`