fix(tours): keep the bike paints, classes and costing to their modules, and pin the profile column to the contract

This commit is contained in:
Maurice
2026-10-19 22:15:22 +02:00
parent b374fdc989
commit f3a39d9452
5 changed files with 22 additions and 6 deletions
@@ -7,7 +7,7 @@ import type { SurfaceClass, TrailClass } from './routeWays';
* own; the map reads them resolved (`useBikePaint`). A kind of way has no
* colour of its own: it is pointed at in the accent.
*/
export const BIKE_PAINT_TOKENS = {
const BIKE_PAINT_TOKENS = {
paved: '--bike-surface-paved',
rough: '--bike-surface-rough',
compacted: '--bike-surface-compacted',
@@ -9,11 +9,11 @@ import { decodePolyline } from '../../Map/transitGeometry';
*/
/** Surfaces from smooth to rough; `unknown` where the data names none. */
export const SURFACE_CLASSES = ['paved', 'rough', 'compacted', 'gravel', 'ground', 'unknown'] as const;
const SURFACE_CLASSES = ['paved', 'rough', 'compacted', 'gravel', 'ground', 'unknown'] as const;
export type SurfaceClass = (typeof SURFACE_CLASSES)[number];
/** Kinds of way, the ones a rider plans by. */
export const WAY_CLASSES = ['cyclePath', 'cycleLane', 'quiet', 'road', 'track', 'path', 'ferry', 'other'] as const;
const WAY_CLASSES = ['cyclePath', 'cycleLane', 'quiet', 'road', 'track', 'path', 'ferry', 'other'] as const;
export type WayClass = (typeof WAY_CLASSES)[number];
/**
@@ -21,7 +21,7 @@ export type WayClass = (typeof WAY_CLASSES)[number];
* (rare, and all of them expert ground); `none` where OpenStreetMap gives the
* way no grade, `unasked` where it could not be asked this time.
*/
export const TRAIL_CLASSES = ['s0', 's1', 's2', 's3', 's4', 'none', 'unasked'] as const;
const TRAIL_CLASSES = ['s0', 's1', 's2', 's3', 's4', 'none', 'unasked'] as const;
export type TrailClass = (typeof TRAIL_CLASSES)[number];
/** Surfaces a road bike's rider calls unpaved (BIK-02): what is no paving of any kind. */
@@ -10,7 +10,7 @@ import { routeWaysFrom, wayIdsOf, type RouteWays } from './routeWays';
/** How long a route has to stay before its ways are asked about, so a route being shaped asks once. */
const SETTLE_MS = 600;
export type RouteWaysStatus = 'idle' | 'loading' | 'ready' | 'failed' | 'offline';
type RouteWaysStatus = 'idle' | 'loading' | 'ready' | 'failed' | 'offline';
export interface TrailGrades {
status: 'loading' | 'ready' | 'failed' | 'offline';
@@ -77,7 +77,7 @@ export function hikingCostingOptions(preferences: HikingPreferences | null | und
* `use_roads` and the bicycle type also set the router's riding speed, so the
* time it gives is the profile's. None of it says a way is rideable or safe.
*/
export const BIKE_COSTING: Record<BikeProfile, Readonly<CostingOptions>> = {
const BIKE_COSTING: Record<BikeProfile, Readonly<CostingOptions>> = {
touring: { bicycle_type: 'Hybrid', use_roads: 0.2, avoid_bad_surfaces: 1 },
road: { bicycle_type: 'Road', use_roads: 0.5, avoid_bad_surfaces: 1 },
gravel: { bicycle_type: 'Cross', use_roads: 0.1, avoid_bad_surfaces: 0.25 },
@@ -0,0 +1,16 @@
import { ToursBikeProfile } from '../../../src/db/entities/Tours.entity';
import { BIKE_PROFILES } from '@trek/shared';
import { describe, expect, it } from 'vitest';
/**
* BIK-01: the bike profile column holds a CHECK list (migration 045200) that
* the entity carries as its enum, and the contract holds its own list. A
* profile one side knows and the other refuses would be a tour the planner
* can save and the database cannot store.
*/
describe('bike profile column matches the contract', () => {
it('knows the same profiles as the bike contract', () => {
expect(Object.values(ToursBikeProfile).sort()).toEqual([...BIKE_PROFILES].sort());
});
});