diff --git a/client/src/components/Planner/DayPlanPlaceSubline.tsx b/client/src/components/Planner/DayPlanPlaceSubline.tsx
index 8d40d4cb1..3db2833d8 100644
--- a/client/src/components/Planner/DayPlanPlaceSubline.tsx
+++ b/client/src/components/Planner/DayPlanPlaceSubline.tsx
@@ -7,9 +7,9 @@ import type { Category, Place } from '../../types';
import { formatTime } from '../../utils/formatters';
import { fs } from '../shared/DialogShell';
import { TourDayRowPills } from '../Tours/TourDayRowFacts';
-import { WalkProgressPill } from '../Tours/walk/WalkStations';
import { movingTimeLabel } from '../Tours/tourFacts';
import { formatPlannedTourDuration, type TourPlannedTimes } from '../Tours/tourPresentation';
+import { WalkProgressPill } from '../Tours/walk/WalkStations';
import { TimePill } from './planParts';
type DayRowTourTimes = TourPlannedTimes & { tour?: TourListItem };
@@ -93,7 +93,14 @@ function SublineDescription({ text, isTourPlace }: { text: string; isTourPlace:
// Second line of a place row in the day plan: time, tour facts or planned
// tour durations, then the clamped description.
-export function DayPlanPlaceSubline({ place, cat, tourTimes, isTourPlace, timeFormat, assignmentId }: DayPlanPlaceSublineProps) {
+export function DayPlanPlaceSubline({
+ place,
+ cat,
+ tourTimes,
+ isTourPlace,
+ timeFormat,
+ assignmentId,
+}: DayPlanPlaceSublineProps) {
const { locale } = useTranslation();
const dayRowTour = tourTimes?.tour;
const description = place.description || place.address || cat?.name;
diff --git a/client/src/components/Tours/planner/TourEditorFields.tsx b/client/src/components/Tours/planner/TourEditorFields.tsx
index b34c7e122..54b3f65a9 100644
--- a/client/src/components/Tours/planner/TourEditorFields.tsx
+++ b/client/src/components/Tours/planner/TourEditorFields.tsx
@@ -79,6 +79,7 @@ export function TourNameField({ planner, locked }: TourFieldProps) {
hike: t('tours.planner.namePlaceholder'),
bike: t('tours.planner.namePlaceholder.bike'),
kayak: t('tours.planner.namePlaceholder.kayak'),
+ walk: t('tours.planner.namePlaceholder.walk'),
}[planner.tourType]
}
className={INPUT}
diff --git a/client/src/components/Tours/planner/TourWaypointList.tsx b/client/src/components/Tours/planner/TourWaypointList.tsx
index 10112b543..3310d2e6d 100644
--- a/client/src/components/Tours/planner/TourWaypointList.tsx
+++ b/client/src/components/Tours/planner/TourWaypointList.tsx
@@ -17,8 +17,8 @@ import {
Navigation,
RefreshCw,
Repeat,
- SquarePen,
Spline,
+ SquarePen,
Trash2,
Unlink,
Waves,
@@ -47,10 +47,10 @@ import { usePaddleRegionLoad } from '../paddle/usePaddleRegionLoad';
import { usePaddleWaypointActions } from '../paddle/usePaddleWaypointActions';
import { usePortageRouting, usePortageWay } from '../paddle/usePortageRouting';
import { RailCard } from '../tourParts';
-import { StationDialog } from './StationDialog';
-import { StationPills } from './stationParts';
import { paddlePlanPoints, paddleRolesById } from './paddlePlanOf';
import type { PaddleLegFailure } from './paddleRouting';
+import { StationDialog } from './StationDialog';
+import { StationPills } from './stationParts';
import { lineLengthM } from './tourFixedLegs';
import { TripPlacePicker } from './TripPlacePicker';
import type { TourPlannerController } from './useTourPlanner';
diff --git a/client/src/components/Tours/planner/tourPlannerWaypoints.ts b/client/src/components/Tours/planner/tourPlannerWaypoints.ts
index 96fa61a86..68021dc15 100644
--- a/client/src/components/Tours/planner/tourPlannerWaypoints.ts
+++ b/client/src/components/Tours/planner/tourPlannerWaypoints.ts
@@ -1,4 +1,11 @@
-import type { PaddleLock, PlannableTourType, StationImportance, TourFixedLeg, TourWaypoint, TourWaypointRole } from '@trek/shared';
+import type {
+ PaddleLock,
+ PlannableTourType,
+ StationImportance,
+ TourFixedLeg,
+ TourWaypoint,
+ TourWaypointRole,
+} from '@trek/shared';
import type { AutoBase } from './paddleRouting';
import { keepJoinedFixedLegs } from './tourFixedLegs';
import { alignFreeLegs } from './tourRouteActions';
diff --git a/client/src/components/Tours/poi/TourPoiSearchCard.tsx b/client/src/components/Tours/poi/TourPoiSearchCard.tsx
index 92807f0a8..68f7795d6 100644
--- a/client/src/components/Tours/poi/TourPoiSearchCard.tsx
+++ b/client/src/components/Tours/poi/TourPoiSearchCard.tsx
@@ -70,13 +70,13 @@ function CategoryPicker({ search }: { search: TourPoiSearch }) {
className="flex w-full min-w-0 items-center gap-2 rounded-[10px] border border-edge bg-surface-input px-2.5 py-1.5 text-start hover:border-content-faint"
>
{t('tours.poi.categories')}:
-
- {chosen.slice(0, 3).map((category) => {
+
+ {chosen.slice(0, 3).map((category, index) => {
const look = TOUR_POI_LOOKS[category];
return (
0 ? '-ms-1' : ''}`}
style={{ color: look.color }}
>
diff --git a/server/src/db/entities/DayAssignments.entity.ts b/server/src/db/entities/DayAssignments.entity.ts
index 140edd2d1..42b842d4e 100644
--- a/server/src/db/entities/DayAssignments.entity.ts
+++ b/server/src/db/entities/DayAssignments.entity.ts
@@ -7,6 +7,7 @@ import { FileLinks } from './FileLinks.entity';
import { Places } from './Places.entity';
import { Reservations } from './Reservations.entity';
import { RoadtripDayBoundaries } from './RoadtripDayBoundaries.entity';
+import { TourStationVisits } from './TourStationVisits.entity';
export class DayAssignments {
[EntityRepositoryType]?: DayAssignmentsRepository;
@@ -33,6 +34,7 @@ export class DayAssignments {
reservations_collection = new Collection(this);
roadtrip_day_boundaries_collection = new Collection(this);
roadtrip_day_boundaries_collection1 = new Collection(this);
+ tour_station_visits_collection = new Collection(this);
}
export const DayAssignmentsSchema = defineEntity({
@@ -62,5 +64,6 @@ export const DayAssignmentsSchema = defineEntity({
reservations_collection: () => p.oneToMany(Reservations).mappedBy('assignment').hidden(),
roadtrip_day_boundaries_collection: () => p.oneToMany(RoadtripDayBoundaries).mappedBy('fromAssignment').hidden(),
roadtrip_day_boundaries_collection1: () => p.oneToMany(RoadtripDayBoundaries).mappedBy('toAssignment').hidden(),
+ tour_station_visits_collection: () => p.oneToMany(TourStationVisits).mappedBy('assignment').hidden(),
},
});
diff --git a/server/src/db/repositories/TourStationVisits.repository.ts b/server/src/db/repositories/TourStationVisits.repository.ts
index 22ce76c58..6967644d4 100644
--- a/server/src/db/repositories/TourStationVisits.repository.ts
+++ b/server/src/db/repositories/TourStationVisits.repository.ts
@@ -85,7 +85,7 @@ export class TourStationVisitsRepository extends TrekRepository ({
status: eb.ref('excluded.status'),
updated_at: eb.fn('CURRENT_TIMESTAMP', []),
- }))
+ })),
)
.execute();
}
@@ -101,10 +101,7 @@ export class TourStationVisitsRepository extends TrekRepository {
- const assignments = this.db()
- .selectFrom('day_assignments')
- .select('id')
- .where('place_id', '=', place_id);
+ const assignments = this.db().selectFrom('day_assignments').select('id').where('place_id', '=', place_id);
let query = this.db().deleteFrom('tour_station_visits').where('assignment_id', 'in', assignments);
if (keep.length > 0) query = query.where('station_key', 'not in', [...keep]);
await query.execute();
diff --git a/server/src/nest/tours/tour-visits.controller.ts b/server/src/nest/tours/tour-visits.controller.ts
index b0e2df09b..6d3b0e3a4 100644
--- a/server/src/nest/tours/tour-visits.controller.ts
+++ b/server/src/nest/tours/tour-visits.controller.ts
@@ -6,7 +6,17 @@ import { ResponseContract } from '../common/response-contract';
import { RequirePermission, TripAccessGuard } from '../permissions/trip-access.guard';
import { TourVisitUpdateDto } from './dto/tour-visit.dto';
import { TourVisitsService } from './tour-visits.service';
-import { BadRequestException, Body, Controller, Get, Headers, Param, ParseIntPipe, Put, UseGuards } from '@nestjs/common';
+import {
+ BadRequestException,
+ Body,
+ Controller,
+ Get,
+ Headers,
+ Param,
+ ParseIntPipe,
+ Put,
+ UseGuards,
+} from '@nestjs/common';
import { stationKeySchema, tourVisitListResponseSchema } from '@trek/shared';
/**
diff --git a/server/src/nest/tours/tour-visits.service.ts b/server/src/nest/tours/tour-visits.service.ts
index ba8c87a5c..af22a773f 100644
--- a/server/src/nest/tours/tour-visits.service.ts
+++ b/server/src/nest/tours/tour-visits.service.ts
@@ -1,7 +1,10 @@
import { TourStationVisits } from '../../db/entities/TourStationVisits.entity';
import { TourWaypoints } from '../../db/entities/TourWaypoints.entity';
import { Tours } from '../../db/entities/Tours.entity';
-import type { TourStationVisitRow, TourStationVisitsRepository } from '../../db/repositories/TourStationVisits.repository';
+import type {
+ TourStationVisitRow,
+ TourStationVisitsRepository,
+} from '../../db/repositories/TourStationVisits.repository';
import type { TourWaypointRow, TourWaypointsRepository } from '../../db/repositories/TourWaypoints.repository';
import type { ToursRepository } from '../../db/repositories/Tours.repository';
import { PlacesService } from '../places/places.service';
diff --git a/server/src/nest/tours/tours.module.ts b/server/src/nest/tours/tours.module.ts
index 3fd0038fe..53a131a75 100644
--- a/server/src/nest/tours/tours.module.ts
+++ b/server/src/nest/tours/tours.module.ts
@@ -1,7 +1,7 @@
import { Places } from '../../db/entities/Places.entity';
-import { TourStationVisits } from '../../db/entities/TourStationVisits.entity';
import { TourObstacleReviews } from '../../db/entities/TourObstacleReviews.entity';
import { TourSources } from '../../db/entities/TourSources.entity';
+import { TourStationVisits } from '../../db/entities/TourStationVisits.entity';
import { TourTypes } from '../../db/entities/TourTypes.entity';
import { TourWaypoints } from '../../db/entities/TourWaypoints.entity';
import { Tours } from '../../db/entities/Tours.entity';
@@ -62,7 +62,15 @@ import { Module } from '@nestjs/common';
WandererModule,
// The obstacle data a paddle tour's checklist reads (PAD-06, PAD-09).
PaddleObstaclesModule,
- MikroOrmModule.forFeature([Tours, TourTypes, TourWaypoints, TourSources, TourObstacleReviews, TourStationVisits, Places]),
+ MikroOrmModule.forFeature([
+ Tours,
+ TourTypes,
+ TourWaypoints,
+ TourSources,
+ TourObstacleReviews,
+ TourStationVisits,
+ Places,
+ ]),
],
controllers: [
TourTakeOverController,
diff --git a/server/src/nest/tours/tours.service.ts b/server/src/nest/tours/tours.service.ts
index 3c9349e3d..4fb98ff83 100644
--- a/server/src/nest/tours/tours.service.ts
+++ b/server/src/nest/tours/tours.service.ts
@@ -1,13 +1,12 @@
-import { randomUUID } from 'node:crypto';
import { Places } from '../../db/entities/Places.entity';
-import { TourTypes } from '../../db/entities/TourTypes.entity';
import { TourStationVisits } from '../../db/entities/TourStationVisits.entity';
+import { TourTypes } from '../../db/entities/TourTypes.entity';
import { TourWaypoints } from '../../db/entities/TourWaypoints.entity';
import { Tours } from '../../db/entities/Tours.entity';
import { TOUR_HIKING_PREFERENCES, TOUR_TRACK_MARKS } from '../../db/json-columns';
import type { PlacesRepository } from '../../db/repositories/Places.repository';
-import type { TourTypesRepository } from '../../db/repositories/TourTypes.repository';
import type { TourStationVisitsRepository } from '../../db/repositories/TourStationVisits.repository';
+import type { TourTypesRepository } from '../../db/repositories/TourTypes.repository';
import type { TourWaypointsRepository } from '../../db/repositories/TourWaypoints.repository';
import type {
TourDayRow,
@@ -56,6 +55,8 @@ import {
wandererDifficultyToSac,
} from '@trek/shared';
+import { randomUUID } from 'node:crypto';
+
export interface ImportGpxAsTourResult {
tours: TourListItem[];
caution: boolean;
diff --git a/server/tests/helpers/tours-repos.ts b/server/tests/helpers/tours-repos.ts
index 2997df884..6e738a490 100644
--- a/server/tests/helpers/tours-repos.ts
+++ b/server/tests/helpers/tours-repos.ts
@@ -1,6 +1,8 @@
+import { TourStationVisits } from '../../src/db/entities/TourStationVisits.entity';
import { TourTypes } from '../../src/db/entities/TourTypes.entity';
import { TourWaypoints } from '../../src/db/entities/TourWaypoints.entity';
import { Tours } from '../../src/db/entities/Tours.entity';
+import type { TourStationVisitsRepository } from '../../src/db/repositories/TourStationVisits.repository';
import type { TourTypesRepository } from '../../src/db/repositories/TourTypes.repository';
import type { TourWaypointsRepository } from '../../src/db/repositories/TourWaypoints.repository';
import type { ToursRepository } from '../../src/db/repositories/Tours.repository';
@@ -26,6 +28,10 @@ export function createTestTourWaypointsRepo(db: Database.Database): Promise t.repo(TourWaypoints));
}
+export function createTestTourStationVisitsRepo(db: Database.Database): Promise {
+ return sharedTestOrm(db).then((t) => t.repo(TourStationVisits));
+}
+
/** A `tours` row for an existing place, with the defaults a GPX import writes. */
export function createTour(
db: Database.Database,
diff --git a/server/tests/unit/nest/tours.gpx.atomic.test.ts b/server/tests/unit/nest/tours.gpx.atomic.test.ts
index 273c5f431..ba2aba093 100644
--- a/server/tests/unit/nest/tours.gpx.atomic.test.ts
+++ b/server/tests/unit/nest/tours.gpx.atomic.test.ts
@@ -59,7 +59,12 @@ import {
createTestCollectionPlacesRepo,
sharedTestOrm,
} from '../../helpers/test-uow';
-import { createTestToursRepo, createTestTourTypesRepo, createTestTourWaypointsRepo } from '../../helpers/tours-repos';
+import {
+ createTestToursRepo,
+ createTestTourStationVisitsRepo,
+ createTestTourTypesRepo,
+ createTestTourWaypointsRepo,
+} from '../../helpers/tours-repos';
import type { EntityClass } from '@mikro-orm/core';
import { Logger } from '@nestjs/common';
@@ -165,6 +170,7 @@ beforeAll(async () => {
await createTestToursRepo(testDb),
await createTestTourTypesRepo(testDb),
await createTestTourWaypointsRepo(testDb),
+ await createTestTourStationVisitsRepo(testDb),
await createTestPlacesRepo(testDb),
buildPlaceImportService(),
noPaddleChecks(),
diff --git a/server/tests/unit/nest/tours.permissions.boundaries.mock-only.test.ts b/server/tests/unit/nest/tours.permissions.boundaries.mock-only.test.ts
index 1ea977d8f..7bdacf3a3 100644
--- a/server/tests/unit/nest/tours.permissions.boundaries.mock-only.test.ts
+++ b/server/tests/unit/nest/tours.permissions.boundaries.mock-only.test.ts
@@ -9,6 +9,7 @@
import 'reflect-metadata';
import type { PlacesRepository } from '../../../src/db/repositories/Places.repository';
+import type { TourStationVisitsRepository } from '../../../src/db/repositories/TourStationVisits.repository';
import type { TourTypesRepository } from '../../../src/db/repositories/TourTypes.repository';
import type { TourWaypointsRepository } from '../../../src/db/repositories/TourWaypoints.repository';
import type { TourProvenanceRow, ToursRepository } from '../../../src/db/repositories/Tours.repository';
@@ -306,6 +307,7 @@ function tourRepositoryFixture(ownerTripId: number) {
toursRepo as unknown as ToursRepository,
{ isEnabled: vi.fn(async () => true) } as unknown as TourTypesRepository,
waypointsRepo as unknown as TourWaypointsRepository,
+ { dropStationsNotIn: vi.fn(async () => undefined) } as unknown as TourStationVisitsRepository,
placesRepo as unknown as PlacesRepository,
buildPlaceImportService(),
noPaddleChecks(),
diff --git a/server/tests/unit/nest/tours.service.permissions.test.ts b/server/tests/unit/nest/tours.service.permissions.test.ts
index 973d8925d..d5306b8ed 100644
--- a/server/tests/unit/nest/tours.service.permissions.test.ts
+++ b/server/tests/unit/nest/tours.service.permissions.test.ts
@@ -6,6 +6,7 @@
* SQLite database is covered by tours.service.test.ts.
*/
import type { PlacesRepository } from '../../../src/db/repositories/Places.repository';
+import type { TourStationVisitsRepository } from '../../../src/db/repositories/TourStationVisits.repository';
import type { TourTypesRepository } from '../../../src/db/repositories/TourTypes.repository';
import type { TourWaypointsRepository } from '../../../src/db/repositories/TourWaypoints.repository';
import type { TourProvenanceRow, ToursRepository } from '../../../src/db/repositories/Tours.repository';
@@ -140,6 +141,7 @@ function makeService() {
toursRepo as unknown as ToursRepository,
{ isEnabled: vi.fn(async () => true) } as unknown as TourTypesRepository,
waypointsRepo as unknown as TourWaypointsRepository,
+ { dropStationsNotIn: vi.fn(async () => undefined) } as unknown as TourStationVisitsRepository,
placesRepo as unknown as PlacesRepository,
buildPlaceImportService(),
noPaddleChecks(),
diff --git a/server/tests/unit/nest/tours.service.test.ts b/server/tests/unit/nest/tours.service.test.ts
index ac9f93a09..80c205bc5 100644
--- a/server/tests/unit/nest/tours.service.test.ts
+++ b/server/tests/unit/nest/tours.service.test.ts
@@ -21,6 +21,7 @@ import { resetTestDb } from '../../helpers/test-db';
import { createTestPlacesRepo, createTestUnitOfWork, sharedTestOrm } from '../../helpers/test-uow';
import {
createTestToursRepo,
+ createTestTourStationVisitsRepo,
createTestTourTypesRepo,
createTestTourWaypointsRepo,
createTour,
@@ -81,6 +82,7 @@ beforeAll(async () => {
await createTestToursRepo(testDb),
await createTestTourTypesRepo(testDb),
await createTestTourWaypointsRepo(testDb),
+ await createTestTourStationVisitsRepo(testDb),
await createTestPlacesRepo(testDb),
buildPlaceImportService(),
noPaddleChecks(),
diff --git a/shared/src/i18n/ar/tours.ts b/shared/src/i18n/ar/tours.ts
index 05f5f36de..759b9945d 100644
--- a/shared/src/i18n/ar/tours.ts
+++ b/shared/src/i18n/ar/tours.ts
@@ -126,6 +126,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'جولة مشي بلا اسم',
'tours.planner.namePlaceholder.bike': 'جولة دراجة بلا اسم',
'tours.planner.namePlaceholder.kayak': 'جولة تجديف بلا اسم',
+ 'tours.planner.namePlaceholder.walk': 'جولة مشي في المدينة بلا اسم',
'tours.planner.mapSearch': 'ابحث عن مكان أو عنوان',
'tours.planner.placePoints': 'تعيين النقاط',
'tours.planner.pointPlaced': 'تم تعيين النقطة',
diff --git a/shared/src/i18n/az/tours.ts b/shared/src/i18n/az/tours.ts
index 8d418a277..85bbe771b 100644
--- a/shared/src/i18n/az/tours.ts
+++ b/shared/src/i18n/az/tours.ts
@@ -113,6 +113,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Adsız yürüş',
'tours.planner.namePlaceholder.bike': 'Adsız velosiped turu',
'tours.planner.namePlaceholder.kayak': 'Adsız avarçəkmə turu',
+ 'tours.planner.namePlaceholder.walk': 'Adsız şəhər gəzintisi',
'tours.planner.mapSearch': 'Yer və ya ünvan axtar',
'tours.planner.placePoints': 'Nöqtə qoy',
'tours.planner.pointPlaced': 'Nöqtə qoyuldu',
diff --git a/shared/src/i18n/br/tours.ts b/shared/src/i18n/br/tours.ts
index c242a9823..5e8919862 100644
--- a/shared/src/i18n/br/tours.ts
+++ b/shared/src/i18n/br/tours.ts
@@ -117,6 +117,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Trilha sem título',
'tours.planner.namePlaceholder.bike': 'Passeio de bicicleta sem nome',
'tours.planner.namePlaceholder.kayak': 'Passeio de remo sem nome',
+ 'tours.planner.namePlaceholder.walk': 'Passeio pela cidade sem nome',
'tours.planner.mapSearch': 'Buscar lugar ou endereço',
'tours.planner.placePoints': 'Marcar pontos',
'tours.planner.pointPlaced': 'Ponto marcado',
diff --git a/shared/src/i18n/ca/tours.ts b/shared/src/i18n/ca/tours.ts
index 1d4f94bb6..17a531377 100644
--- a/shared/src/i18n/ca/tours.ts
+++ b/shared/src/i18n/ca/tours.ts
@@ -117,6 +117,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Ruta de senderisme sense títol',
'tours.planner.namePlaceholder.bike': 'Ruta en bici sense nom',
'tours.planner.namePlaceholder.kayak': 'Sortida en caiac sense nom',
+ 'tours.planner.namePlaceholder.walk': 'Passejada per la ciutat sense nom',
'tours.planner.mapSearch': 'Cerca un lloc o una adreça',
'tours.planner.placePoints': 'Posar punts',
'tours.planner.pointPlaced': 'Punt posat',
diff --git a/shared/src/i18n/cs/tours.ts b/shared/src/i18n/cs/tours.ts
index 11fdc06ae..6e7af26c2 100644
--- a/shared/src/i18n/cs/tours.ts
+++ b/shared/src/i18n/cs/tours.ts
@@ -116,6 +116,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Túra bez názvu',
'tours.planner.namePlaceholder.bike': 'Cyklotúra bez názvu',
'tours.planner.namePlaceholder.kayak': 'Vodácký výlet bez názvu',
+ 'tours.planner.namePlaceholder.walk': 'Procházka městem bez názvu',
'tours.planner.mapSearch': 'Hledat místo nebo adresu',
'tours.planner.placePoints': 'Přidávat body',
'tours.planner.pointPlaced': 'Bod přidán',
diff --git a/shared/src/i18n/de/tours.ts b/shared/src/i18n/de/tours.ts
index 644298a7b..80d9b15a8 100644
--- a/shared/src/i18n/de/tours.ts
+++ b/shared/src/i18n/de/tours.ts
@@ -118,6 +118,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Wanderung ohne Titel',
'tours.planner.namePlaceholder.bike': 'Radtour ohne Namen',
'tours.planner.namePlaceholder.kayak': 'Paddeltour ohne Namen',
+ 'tours.planner.namePlaceholder.walk': 'Stadtspaziergang ohne Namen',
'tours.planner.mapSearch': 'Ort oder Adresse suchen',
'tours.planner.placePoints': 'Punkte setzen',
'tours.planner.pointPlaced': 'Punkt gesetzt',
diff --git a/shared/src/i18n/en/tours.ts b/shared/src/i18n/en/tours.ts
index 4b1a6b73f..dad8861d3 100644
--- a/shared/src/i18n/en/tours.ts
+++ b/shared/src/i18n/en/tours.ts
@@ -113,6 +113,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Untitled hike',
'tours.planner.namePlaceholder.bike': 'Untitled bike tour',
'tours.planner.namePlaceholder.kayak': 'Untitled paddle tour',
+ 'tours.planner.namePlaceholder.walk': 'Untitled city walk',
'tours.planner.mapSearch': 'Search place or address',
'tours.planner.placePoints': 'Set points',
'tours.planner.pointPlaced': 'Point set',
diff --git a/shared/src/i18n/es/tours.ts b/shared/src/i18n/es/tours.ts
index e871b43d0..b15dac439 100644
--- a/shared/src/i18n/es/tours.ts
+++ b/shared/src/i18n/es/tours.ts
@@ -118,6 +118,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Ruta de senderismo sin título',
'tours.planner.namePlaceholder.bike': 'Ruta en bici sin nombre',
'tours.planner.namePlaceholder.kayak': 'Ruta en kayak sin nombre',
+ 'tours.planner.namePlaceholder.walk': 'Paseo por la ciudad sin nombre',
'tours.planner.mapSearch': 'Buscar lugar o dirección',
'tours.planner.placePoints': 'Colocar puntos',
'tours.planner.pointPlaced': 'Punto colocado',
diff --git a/shared/src/i18n/et/tours.ts b/shared/src/i18n/et/tours.ts
index 42edb375a..9c7d2db01 100644
--- a/shared/src/i18n/et/tours.ts
+++ b/shared/src/i18n/et/tours.ts
@@ -114,6 +114,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Nimetu matk',
'tours.planner.namePlaceholder.bike': 'Nimetu rattamatk',
'tours.planner.namePlaceholder.kayak': 'Nimetu aerutusmatk',
+ 'tours.planner.namePlaceholder.walk': 'Nimetu linnajalutuskäik',
'tours.planner.mapSearch': 'Otsi kohta või aadressi',
'tours.planner.placePoints': 'Lisa punkte',
'tours.planner.pointPlaced': 'Punkt lisatud',
diff --git a/shared/src/i18n/fr/tours.ts b/shared/src/i18n/fr/tours.ts
index 3e1133ca2..ad8f70b30 100644
--- a/shared/src/i18n/fr/tours.ts
+++ b/shared/src/i18n/fr/tours.ts
@@ -120,6 +120,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Randonnée sans titre',
'tours.planner.namePlaceholder.bike': 'Sortie vélo sans titre',
'tours.planner.namePlaceholder.kayak': 'Sortie en pagaie sans titre',
+ 'tours.planner.namePlaceholder.walk': 'Balade en ville sans titre',
'tours.planner.mapSearch': 'Rechercher un lieu ou une adresse',
'tours.planner.placePoints': 'Placer des points',
'tours.planner.pointPlaced': 'Point placé',
diff --git a/shared/src/i18n/gr/tours.ts b/shared/src/i18n/gr/tours.ts
index ddcd3b758..41447f068 100644
--- a/shared/src/i18n/gr/tours.ts
+++ b/shared/src/i18n/gr/tours.ts
@@ -118,6 +118,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Πεζοπορία χωρίς όνομα',
'tours.planner.namePlaceholder.bike': 'Ποδηλατάδα χωρίς όνομα',
'tours.planner.namePlaceholder.kayak': 'Κουπί χωρίς όνομα',
+ 'tours.planner.namePlaceholder.walk': 'Βόλτα στην πόλη χωρίς όνομα',
'tours.planner.mapSearch': 'Αναζήτηση τόπου ή διεύθυνσης',
'tours.planner.placePoints': 'Τοποθέτηση σημείων',
'tours.planner.pointPlaced': 'Το σημείο τοποθετήθηκε',
diff --git a/shared/src/i18n/hu/tours.ts b/shared/src/i18n/hu/tours.ts
index 02a5c0b9c..53bc4e9ce 100644
--- a/shared/src/i18n/hu/tours.ts
+++ b/shared/src/i18n/hu/tours.ts
@@ -114,6 +114,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Névtelen gyalogtúra',
'tours.planner.namePlaceholder.bike': 'Névtelen kerékpártúra',
'tours.planner.namePlaceholder.kayak': 'Névtelen evezőstúra',
+ 'tours.planner.namePlaceholder.walk': 'Névtelen városi séta',
'tours.planner.mapSearch': 'Hely vagy cím keresése',
'tours.planner.placePoints': 'Pontok elhelyezése',
'tours.planner.pointPlaced': 'Pont elhelyezve',
diff --git a/shared/src/i18n/id/tours.ts b/shared/src/i18n/id/tours.ts
index 446319e79..86445fff0 100644
--- a/shared/src/i18n/id/tours.ts
+++ b/shared/src/i18n/id/tours.ts
@@ -112,6 +112,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Pendakian tanpa judul',
'tours.planner.namePlaceholder.bike': 'Tur sepeda tanpa nama',
'tours.planner.namePlaceholder.kayak': 'Tur dayung tanpa nama',
+ 'tours.planner.namePlaceholder.walk': 'Jalan kaki kota tanpa nama',
'tours.planner.mapSearch': 'Cari tempat atau alamat',
'tours.planner.placePoints': 'Tempatkan titik',
'tours.planner.pointPlaced': 'Titik ditempatkan',
diff --git a/shared/src/i18n/it/tours.ts b/shared/src/i18n/it/tours.ts
index 97083c9fc..51c7edec4 100644
--- a/shared/src/i18n/it/tours.ts
+++ b/shared/src/i18n/it/tours.ts
@@ -116,6 +116,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Escursione a piedi senza titolo',
'tours.planner.namePlaceholder.bike': 'Giro in bici senza nome',
'tours.planner.namePlaceholder.kayak': 'Uscita in canoa senza nome',
+ 'tours.planner.namePlaceholder.walk': 'Passeggiata in città senza nome',
'tours.planner.mapSearch': 'Cerca luogo o indirizzo',
'tours.planner.placePoints': 'Posiziona punti',
'tours.planner.pointPlaced': 'Punto posizionato',
diff --git a/shared/src/i18n/ja/tours.ts b/shared/src/i18n/ja/tours.ts
index 45c7ca170..47ea2cef8 100644
--- a/shared/src/i18n/ja/tours.ts
+++ b/shared/src/i18n/ja/tours.ts
@@ -110,6 +110,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': '名前のないハイキング',
'tours.planner.namePlaceholder.bike': '名前のないサイクリング',
'tours.planner.namePlaceholder.kayak': '名前のないパドルツアー',
+ 'tours.planner.namePlaceholder.walk': '名前のない街歩き',
'tours.planner.mapSearch': '場所や住所を検索',
'tours.planner.placePoints': 'ポイントを配置',
'tours.planner.pointPlaced': 'ポイントを配置しました',
diff --git a/shared/src/i18n/ko/tours.ts b/shared/src/i18n/ko/tours.ts
index c8f6735ec..bee2918c6 100644
--- a/shared/src/i18n/ko/tours.ts
+++ b/shared/src/i18n/ko/tours.ts
@@ -108,6 +108,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': '이름 없는 하이킹',
'tours.planner.namePlaceholder.bike': '이름 없는 자전거 투어',
'tours.planner.namePlaceholder.kayak': '이름 없는 패들 투어',
+ 'tours.planner.namePlaceholder.walk': '이름 없는 도시 산책',
'tours.planner.mapSearch': '장소 또는 주소 검색',
'tours.planner.placePoints': '지점 찍기',
'tours.planner.pointPlaced': '지점을 찍었습니다',
diff --git a/shared/src/i18n/nl/tours.ts b/shared/src/i18n/nl/tours.ts
index 30f8cba04..a68097d78 100644
--- a/shared/src/i18n/nl/tours.ts
+++ b/shared/src/i18n/nl/tours.ts
@@ -118,6 +118,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Naamloze wandeling',
'tours.planner.namePlaceholder.bike': 'Fietstocht zonder naam',
'tours.planner.namePlaceholder.kayak': 'Peddeltocht zonder naam',
+ 'tours.planner.namePlaceholder.walk': 'Stadswandeling zonder naam',
'tours.planner.mapSearch': 'Zoek plaats of adres',
'tours.planner.placePoints': 'Punten plaatsen',
'tours.planner.pointPlaced': 'Punt geplaatst',
diff --git a/shared/src/i18n/pl/tours.ts b/shared/src/i18n/pl/tours.ts
index 0fe21cf39..529567b6b 100644
--- a/shared/src/i18n/pl/tours.ts
+++ b/shared/src/i18n/pl/tours.ts
@@ -124,6 +124,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Wędrówka bez nazwy',
'tours.planner.namePlaceholder.bike': 'Wycieczka rowerowa bez nazwy',
'tours.planner.namePlaceholder.kayak': 'Spływ bez nazwy',
+ 'tours.planner.namePlaceholder.walk': 'Spacer po mieście bez nazwy',
'tours.planner.mapSearch': 'Szukaj miejsca lub adresu',
'tours.planner.placePoints': 'Ustawiaj punkty',
'tours.planner.pointPlaced': 'Punkt ustawiony',
diff --git a/shared/src/i18n/ru/tours.ts b/shared/src/i18n/ru/tours.ts
index c4216a7dd..dae1d4b18 100644
--- a/shared/src/i18n/ru/tours.ts
+++ b/shared/src/i18n/ru/tours.ts
@@ -125,6 +125,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Поход без названия',
'tours.planner.namePlaceholder.bike': 'Велопоездка без названия',
'tours.planner.namePlaceholder.kayak': 'Сплав без названия',
+ 'tours.planner.namePlaceholder.walk': 'Прогулка по городу без названия',
'tours.planner.mapSearch': 'Поиск места или адреса',
'tours.planner.placePoints': 'Ставить точки',
'tours.planner.pointPlaced': 'Точка поставлена',
diff --git a/shared/src/i18n/sk/tours.ts b/shared/src/i18n/sk/tours.ts
index 35c9d1b4a..397f6df63 100644
--- a/shared/src/i18n/sk/tours.ts
+++ b/shared/src/i18n/sk/tours.ts
@@ -117,6 +117,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Pešia túra bez názvu',
'tours.planner.namePlaceholder.bike': 'Cyklotúra bez názvu',
'tours.planner.namePlaceholder.kayak': 'Vodácky výlet bez názvu',
+ 'tours.planner.namePlaceholder.walk': 'Prechádzka mestom bez názvu',
'tours.planner.mapSearch': 'Hľadať miesto alebo adresu',
'tours.planner.placePoints': 'Pridávať body',
'tours.planner.pointPlaced': 'Bod pridaný',
diff --git a/shared/src/i18n/sv/tours.ts b/shared/src/i18n/sv/tours.ts
index 834725954..02b3bf304 100644
--- a/shared/src/i18n/sv/tours.ts
+++ b/shared/src/i18n/sv/tours.ts
@@ -113,6 +113,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Vandring utan namn',
'tours.planner.namePlaceholder.bike': 'Cykeltur utan namn',
'tours.planner.namePlaceholder.kayak': 'Paddeltur utan namn',
+ 'tours.planner.namePlaceholder.walk': 'Stadspromenad utan namn',
'tours.planner.mapSearch': 'Sök plats eller adress',
'tours.planner.placePoints': 'Sätt punkter',
'tours.planner.pointPlaced': 'Punkt satt',
diff --git a/shared/src/i18n/th/tours.ts b/shared/src/i18n/th/tours.ts
index 6db7489f9..c468fcc6c 100644
--- a/shared/src/i18n/th/tours.ts
+++ b/shared/src/i18n/th/tours.ts
@@ -107,6 +107,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'เส้นทางเดินป่าที่ยังไม่มีชื่อ',
'tours.planner.namePlaceholder.bike': 'ทริปจักรยานไม่มีชื่อ',
'tours.planner.namePlaceholder.kayak': 'ทริปพายเรือไม่มีชื่อ',
+ 'tours.planner.namePlaceholder.walk': 'การเดินเที่ยวเมืองที่ยังไม่มีชื่อ',
'tours.planner.mapSearch': 'ค้นหาสถานที่หรือที่อยู่',
'tours.planner.placePoints': 'วางจุด',
'tours.planner.pointPlaced': 'วางจุดแล้ว',
diff --git a/shared/src/i18n/tours-locales.spec.ts b/shared/src/i18n/tours-locales.spec.ts
index fa814cb47..2eb318d3f 100644
--- a/shared/src/i18n/tours-locales.spec.ts
+++ b/shared/src/i18n/tours-locales.spec.ts
@@ -20,7 +20,7 @@ const tokens = (value: string) => [...value.matchAll(/\{[^{}]+\}/g)].map((match)
// placeholder spec; here only the general keys are.
const groups = pluralGroups(Object.keys(english));
const generalKeys = (keys: string[]) => keys.filter((key) => !pluralFormOf(key, groups));
-const GENERAL_KEY_COUNT = 765;
+const GENERAL_KEY_COUNT = 766;
function literalKeys(source: string): string[] {
const tree = ts.createSourceFile('locale.ts', source, ts.ScriptTarget.Latest, true);
diff --git a/shared/src/i18n/tr/tours.ts b/shared/src/i18n/tr/tours.ts
index 32831b96c..f2fcdd516 100644
--- a/shared/src/i18n/tr/tours.ts
+++ b/shared/src/i18n/tr/tours.ts
@@ -113,6 +113,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Adsız doğa yürüyüşü',
'tours.planner.namePlaceholder.bike': 'Adsız bisiklet turu',
'tours.planner.namePlaceholder.kayak': 'Adsız kürek turu',
+ 'tours.planner.namePlaceholder.walk': 'Adsız şehir yürüyüşü',
'tours.planner.mapSearch': 'Yer veya adres ara',
'tours.planner.placePoints': 'Nokta koy',
'tours.planner.pointPlaced': 'Nokta kondu',
diff --git a/shared/src/i18n/uk/tours.ts b/shared/src/i18n/uk/tours.ts
index 5b8d50879..04aae5857 100644
--- a/shared/src/i18n/uk/tours.ts
+++ b/shared/src/i18n/uk/tours.ts
@@ -122,6 +122,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Похід без назви',
'tours.planner.namePlaceholder.bike': 'Велопоїздка без назви',
'tours.planner.namePlaceholder.kayak': 'Сплав без назви',
+ 'tours.planner.namePlaceholder.walk': 'Прогулянка містом без назви',
'tours.planner.mapSearch': 'Пошук місця або адреси',
'tours.planner.placePoints': 'Ставити точки',
'tours.planner.pointPlaced': 'Точку поставлено',
diff --git a/shared/src/i18n/vi/tours.ts b/shared/src/i18n/vi/tours.ts
index a63052ff8..857d13748 100644
--- a/shared/src/i18n/vi/tours.ts
+++ b/shared/src/i18n/vi/tours.ts
@@ -114,6 +114,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': 'Chuyến đi bộ đường dài chưa có tên',
'tours.planner.namePlaceholder.bike': 'Chuyến đạp xe chưa đặt tên',
'tours.planner.namePlaceholder.kayak': 'Chuyến chèo thuyền chưa đặt tên',
+ 'tours.planner.namePlaceholder.walk': 'Chuyến dạo phố chưa đặt tên',
'tours.planner.mapSearch': 'Tìm địa điểm hoặc địa chỉ',
'tours.planner.placePoints': 'Đặt điểm',
'tours.planner.pointPlaced': 'Đã đặt điểm',
diff --git a/shared/src/i18n/zh-TW/tours.ts b/shared/src/i18n/zh-TW/tours.ts
index 707aab9be..77642f2eb 100644
--- a/shared/src/i18n/zh-TW/tours.ts
+++ b/shared/src/i18n/zh-TW/tours.ts
@@ -103,6 +103,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': '未命名的健行',
'tours.planner.namePlaceholder.bike': '未命名的騎乘',
'tours.planner.namePlaceholder.kayak': '未命名的划船行程',
+ 'tours.planner.namePlaceholder.walk': '未命名的城市漫步',
'tours.planner.mapSearch': '搜尋地點或地址',
'tours.planner.placePoints': '放置點',
'tours.planner.pointPlaced': '已放置點',
diff --git a/shared/src/i18n/zh/tours.ts b/shared/src/i18n/zh/tours.ts
index 4319c87a2..12ec91138 100644
--- a/shared/src/i18n/zh/tours.ts
+++ b/shared/src/i18n/zh/tours.ts
@@ -104,6 +104,7 @@ const tours: TranslationStrings = {
'tours.planner.namePlaceholder': '未命名的徒步活动',
'tours.planner.namePlaceholder.bike': '未命名的骑行',
'tours.planner.namePlaceholder.kayak': '未命名的划船行程',
+ 'tours.planner.namePlaceholder.walk': '未命名的城市漫步',
'tours.planner.mapSearch': '搜索地点或地址',
'tours.planner.placePoints': '放置点',
'tours.planner.pointPlaced': '已放置点',
diff --git a/shared/src/tours/tour-walk.schema.ts b/shared/src/tours/tour-walk.schema.ts
index a9ee9c54e..2c4747bfd 100644
--- a/shared/src/tours/tour-walk.schema.ts
+++ b/shared/src/tours/tour-walk.schema.ts
@@ -23,9 +23,7 @@ export const tourVisitListResponseSchema = z.object({ visits: z.array(tourVisitS
export type TourVisitListResponse = z.infer;
/** Planned takes a station back to where it started: nothing visited, nothing skipped. */
-export const tourVisitUpdateRequestSchema = z
- .object({ status: z.enum(['planned', ...TOUR_VISIT_STATUSES]) })
- .strict();
+export const tourVisitUpdateRequestSchema = z.object({ status: z.enum(['planned', ...TOUR_VISIT_STATUSES]) }).strict();
export type TourVisitUpdateRequest = z.infer;
/**
@@ -34,7 +32,9 @@ export type TourVisitUpdateRequest = z.infer;
/** A stop as the link names it; a stop without coordinates in the link is not guessed at. */