From b71e32cf12448c9c213d3ac227fb57f7d421f177 Mon Sep 17 00:00:00 2001 From: Maurice Date: Tue, 20 Oct 2026 22:13:25 +0200 Subject: [PATCH] fix(tours): clear the last lint, knip and wiring findings of the follow-up The unused day pills go, a client-only type stops being exported, the place form context moves out of the page hook so its view model stays the first object it returns, the place form test helpers split the stub component from the plain setup, and four router files are formatted. --- client/src/api/skiRoute.ts | 4 +- .../Planner/PlaceFormModal.test.tsx | 2 +- .../Planner/PlaceFormModal.tour.test.tsx | 2 +- .../components/Tours/planner/skiRouting.ts | 2 +- .../components/Tours/ski/SkiRouteOptions.tsx | 2 +- client/src/components/Tours/tourParts.tsx | 42 ------------------- .../src/pages/tripPlanner/placeFormContext.ts | 18 ++++++++ .../pages/tripPlanner/useTripPlannerPage.ts | 19 +-------- client/src/repo/skiRouteRepo.ts | 2 +- client/tests/helpers/placeForm.tsx | 27 ------------ client/tests/helpers/placeFormSetup.ts | 26 ++++++++++++ server/src/nest/maps/trek-places.client.ts | 2 +- 12 files changed, 52 insertions(+), 96 deletions(-) create mode 100644 client/src/pages/tripPlanner/placeFormContext.ts create mode 100644 client/tests/helpers/placeFormSetup.ts diff --git a/client/src/api/skiRoute.ts b/client/src/api/skiRoute.ts index 79b7ac770..d262f165d 100644 --- a/client/src/api/skiRoute.ts +++ b/client/src/api/skiRoute.ts @@ -4,7 +4,5 @@ import apiClient from './client'; /** The piste router: a ski tour's legs planned on the piste data the server keeps, held to the contract. */ export const skiRouteApi = { plan: (tripId: number | string, request: SkiRouteInput, signal?: AbortSignal): Promise => - apiClient - .post(`/trips/${tripId}/ski/route`, request, { signal }) - .then((r) => skiRouteResponseSchema.parse(r.data)), + apiClient.post(`/trips/${tripId}/ski/route`, request, { signal }).then((r) => skiRouteResponseSchema.parse(r.data)), }; diff --git a/client/src/components/Planner/PlaceFormModal.test.tsx b/client/src/components/Planner/PlaceFormModal.test.tsx index 47bfb6925..c7a7c73a6 100644 --- a/client/src/components/Planner/PlaceFormModal.test.tsx +++ b/client/src/components/Planner/PlaceFormModal.test.tsx @@ -11,7 +11,7 @@ import { usePermissionsStore } from '../../store/permissionsStore'; import { usePluginStore } from '../../store/pluginStore'; import { resetAllStores, seedStore } from '../../../tests/helpers/store'; import { buildUser, buildTrip, buildPlace, buildCategory, buildAssignment } from '../../../tests/helpers/factories'; -import { placeFormProps, seedPlaceFormStores } from '../../../tests/helpers/placeForm'; +import { placeFormProps, seedPlaceFormStores } from '../../../tests/helpers/placeFormSetup'; import PlaceFormModal from './PlaceFormModal'; // CustomTimePicker as a plain text input, see TimePickerStub. diff --git a/client/src/components/Planner/PlaceFormModal.tour.test.tsx b/client/src/components/Planner/PlaceFormModal.tour.test.tsx index 3b1fa0cd6..32a6a0c8b 100644 --- a/client/src/components/Planner/PlaceFormModal.tour.test.tsx +++ b/client/src/components/Planner/PlaceFormModal.tour.test.tsx @@ -2,7 +2,7 @@ import type { TourListItem } from '@trek/shared'; import { buildAssignment, buildPlace } from '../../../tests/helpers/factories'; -import { placeFormProps, seedPlaceFormStores } from '../../../tests/helpers/placeForm'; +import { placeFormProps, seedPlaceFormStores } from '../../../tests/helpers/placeFormSetup'; import { render, screen } from '../../../tests/helpers/render'; import { resetAllStores } from '../../../tests/helpers/store'; import PlaceFormModal from './PlaceFormModal'; diff --git a/client/src/components/Tours/planner/skiRouting.ts b/client/src/components/Tours/planner/skiRouting.ts index 00b0187e1..cceb04daa 100644 --- a/client/src/components/Tours/planner/skiRouting.ts +++ b/client/src/components/Tours/planner/skiRouting.ts @@ -2,8 +2,8 @@ import type { SkiColourScheme, SkiFailCode, SkiFailHint, SkiRouteLeg, SkiRoutePo import { skiRouteRepo, type SkiLegAnswer, type SkiPlanAnswer } from '../../../repo/skiRouteRepo'; import type { SkiChoice } from '../ski/skiChoice'; import { densifyLine, lineLengthM } from './tourFixedLegs'; -import { isTourRoute } from './tourRouteOutcome'; import { freeLegBetween } from './tourRouteActions'; +import { isTourRoute } from './tourRouteOutcome'; import { drawnTour, routeWalkingTour, type TourRoutePart, type TourRouteResult } from './tourRouting'; /** diff --git a/client/src/components/Tours/ski/SkiRouteOptions.tsx b/client/src/components/Tours/ski/SkiRouteOptions.tsx index 44970aa9c..44083aff1 100644 --- a/client/src/components/Tours/ski/SkiRouteOptions.tsx +++ b/client/src/components/Tours/ski/SkiRouteOptions.tsx @@ -6,11 +6,11 @@ import { SKI_ROUTE_PRIORITIES, skiOptionsReadBy, skiUsesLifts, + type SkiRouteOptions as Options, type SkiDifficulty, type SkiDiscipline, type SkiLiftClass, type SkiPace, - type SkiRouteOptions as Options, type SkiRouteOptionsInput, type SkiRoutePriority, } from '@trek/shared'; diff --git a/client/src/components/Tours/tourParts.tsx b/client/src/components/Tours/tourParts.tsx index c181d137b..3b831a9cc 100644 --- a/client/src/components/Tours/tourParts.tsx +++ b/client/src/components/Tours/tourParts.tsx @@ -140,48 +140,6 @@ export function TourActivityBadge({ tour }: { tour: Pick dayMenuLabel(day, index, t, locale) - const shown = tourDays.length > limit ? tourDays.slice(0, limit - 1) : tourDays - const folded = tourDays.slice(shown.length) - const foldedLabel = folded.map(fullLabel).join(', ') - return ( - <> - {shown.map((tourDay) => ( - - - }> - {t('dayplan.dayN', { n: tourDay.index + 1 })} - - - ))} - {folded.length > 0 && ( - - - +{folded.length} - - - - )} - - ) -} - /** * The days a tour is on, as the detail shows them: one box per day in the look * of the measures below, so the days and the numbers read as one block. The diff --git a/client/src/pages/tripPlanner/placeFormContext.ts b/client/src/pages/tripPlanner/placeFormContext.ts new file mode 100644 index 000000000..6a95c3474 --- /dev/null +++ b/client/src/pages/tripPlanner/placeFormContext.ts @@ -0,0 +1,18 @@ +import type { TourListItem } from '@trek/shared'; +import type { Assignment } from '../../types'; + +/** + * The place form's day context: while a place is edited, the whole day plan to check its times + * against and, TIME-02, the tour that place is, whose end the time fields work out from its start. + */ +export function placeFormContext( + editingPlace: { id: number } | null, + assignments: Record, + tours: readonly TourListItem[] +): { dayAssignments: Assignment[]; tour: TourListItem | null } { + if (!editingPlace) return { dayAssignments: [], tour: null }; + return { + dayAssignments: Object.values(assignments).flat(), + tour: tours.find((item) => item.place_id === editingPlace.id) ?? null, + }; +} diff --git a/client/src/pages/tripPlanner/useTripPlannerPage.ts b/client/src/pages/tripPlanner/useTripPlannerPage.ts index f85dc6268..91bb9c489 100644 --- a/client/src/pages/tripPlanner/useTripPlannerPage.ts +++ b/client/src/pages/tripPlanner/useTripPlannerPage.ts @@ -1,6 +1,4 @@ import { useCallback, useEffect, useMemo, useRef } from 'react' -import type { TourListItem } from '@trek/shared' -import type { Assignment } from '../../types' import { usePoiExplore, type Bbox } from '../../components/Map/usePoiExplore' import { placeActions } from '../../components/Planner/usePlaceActions' import { tourRouteStyle } from '../../components/Tours/planner/tourRouteStyle' @@ -17,22 +15,7 @@ import { usePlannerFocusPoints } from './usePlannerFocusPoints' import { useTripPlanner } from './useTripPlanner' import { useTourWorkspace } from './useTourWorkspace' import { mapFocusKeyOf, offeredWaysOf, paddleLineOf } from './tourMapFocus' - -/** - * The place form's day context: while a place is edited, the whole day plan to check its times - * against and, TIME-02, the tour that place is, whose end the time fields work out from its start. - */ -function placeFormContext( - editingPlace: { id: number } | null, - assignments: Record, - tours: readonly TourListItem[], -): { dayAssignments: Assignment[]; tour: TourListItem | null } { - if (!editingPlace) return { dayAssignments: [], tour: null } - return { - dayAssignments: Object.values(assignments).flat(), - tour: tours.find(item => item.place_id === editingPlace.id) ?? null, - } -} +import { placeFormContext } from './placeFormContext' export function useTripPlannerPage() { const planner = useTripPlanner() diff --git a/client/src/repo/skiRouteRepo.ts b/client/src/repo/skiRouteRepo.ts index 33f7c240e..be1db3937 100644 --- a/client/src/repo/skiRouteRepo.ts +++ b/client/src/repo/skiRouteRepo.ts @@ -3,8 +3,8 @@ import { type SkiColourScheme, type SkiDiscipline, type SkiRouteLeg, - type SkiRoutePoint, type SkiRouteOptionsInput, + type SkiRoutePoint, type SkiRouteResponse, type SkiTechnique, } from '@trek/shared'; diff --git a/client/tests/helpers/placeForm.tsx b/client/tests/helpers/placeForm.tsx index d7b99e0b3..5816204a8 100644 --- a/client/tests/helpers/placeForm.tsx +++ b/client/tests/helpers/placeForm.tsx @@ -1,10 +1,3 @@ -import { vi } from 'vitest'; - -import { useAuthStore } from '../../src/store/authStore'; -import { useTripStore } from '../../src/store/tripStore'; -import { buildTrip, buildUser } from './factories'; -import { seedStore } from './store'; - /** * Stands in for CustomTimePicker in the place form's tests: a plain text input * instead of the portal-heavy picker. The aria props land on the input, as the @@ -34,23 +27,3 @@ export function TimePickerStub({ /> ); } - -/** The props an open place form gets when a test sets none: a new place in trip 1. */ -export const placeFormProps = { - isOpen: true, - onClose: vi.fn(), - onSave: vi.fn(), - place: null, - prefillCoords: null, - tripId: 1, - categories: [], - onCategoryCreated: vi.fn(), - assignmentId: null, - dayAssignments: [], -}; - -/** A signed-in user without a maps key, in trip 1: what the form reads before it renders. */ -export function seedPlaceFormStores() { - seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true, hasMapsKey: false }); - seedStore(useTripStore, { trip: buildTrip({ id: 1 }) }); -} diff --git a/client/tests/helpers/placeFormSetup.ts b/client/tests/helpers/placeFormSetup.ts new file mode 100644 index 000000000..31c2e8f08 --- /dev/null +++ b/client/tests/helpers/placeFormSetup.ts @@ -0,0 +1,26 @@ +import { vi } from 'vitest'; + +import { useAuthStore } from '../../src/store/authStore'; +import { useTripStore } from '../../src/store/tripStore'; +import { buildTrip, buildUser } from './factories'; +import { seedStore } from './store'; + +/** The props an open place form gets when a test sets none: a new place in trip 1. */ +export const placeFormProps = { + isOpen: true, + onClose: vi.fn(), + onSave: vi.fn(), + place: null, + prefillCoords: null, + tripId: 1, + categories: [], + onCategoryCreated: vi.fn(), + assignmentId: null, + dayAssignments: [], +}; + +/** A signed-in user without a maps key, in trip 1: what the form reads before it renders. */ +export function seedPlaceFormStores() { + seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true, hasMapsKey: false }); + seedStore(useTripStore, { trip: buildTrip({ id: 1 }) }); +} diff --git a/server/src/nest/maps/trek-places.client.ts b/server/src/nest/maps/trek-places.client.ts index aba56699d..6930fb8a7 100644 --- a/server/src/nest/maps/trek-places.client.ts +++ b/server/src/nest/maps/trek-places.client.ts @@ -750,7 +750,7 @@ const trekWayTagsSchema = z.object({ tags: osmTagsSchema.default({}), }); /** One way of the ways layer, as `way(id:...);out tags;` answers it: only the keys the layer keeps. */ -export type TrekWayTags = z.infer; +type TrekWayTags = z.infer; const pointSchema = z.object({ lat: z.number().min(-90).max(90), lon: z.number().min(-180).max(180) }); const memberSchema = z.object({ type: z.enum(['node', 'way', 'relation']), ref: osmIdSchema, role: z.string() });