mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 14:36:31 +00:00
fix(tours): city walk gates: entity collection, formatting, a walk's name hint and the tests' visits repository
The day assignments entity carries the station visits that hang off it, as the generator writes it. A city walk gets its own placeholder for an untitled name, the tour service tests hand in the visits repository the service now takes, and the files the walk touched are formatted. The theme icons of the place search overlap by their start margin, so they stack the same way in both reading directions.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
<span className="sr-only">{t('tours.poi.categories')}: </span>
|
||||
<span className="flex flex-none -space-x-1 rtl:space-x-reverse" aria-hidden="true">
|
||||
{chosen.slice(0, 3).map((category) => {
|
||||
<span className="flex flex-none" aria-hidden="true">
|
||||
{chosen.slice(0, 3).map((category, index) => {
|
||||
const look = TOUR_POI_LOOKS[category];
|
||||
return (
|
||||
<span
|
||||
key={category}
|
||||
className="grid h-5 w-5 place-items-center rounded-full bg-surface-card ring-2 ring-[color:var(--bg-card)]"
|
||||
className={`grid h-5 w-5 place-items-center rounded-full bg-surface-card ring-2 ring-[color:var(--bg-card)] ${index > 0 ? '-ms-1' : ''}`}
|
||||
style={{ color: look.color }}
|
||||
>
|
||||
<look.Icon size={11} strokeWidth={2.2} />
|
||||
|
||||
@@ -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<Reservations>(this);
|
||||
roadtrip_day_boundaries_collection = new Collection<RoadtripDayBoundaries>(this);
|
||||
roadtrip_day_boundaries_collection1 = new Collection<RoadtripDayBoundaries>(this);
|
||||
tour_station_visits_collection = new Collection<TourStationVisits>(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(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ export class TourStationVisitsRepository extends TrekRepository<TourStationVisit
|
||||
oc.columns(['assignment_id', 'station_key']).doUpdateSet((eb) => ({
|
||||
status: eb.ref('excluded.status'),
|
||||
updated_at: eb.fn<string>('CURRENT_TIMESTAMP', []),
|
||||
}))
|
||||
})),
|
||||
)
|
||||
.execute();
|
||||
}
|
||||
@@ -101,10 +101,7 @@ export class TourStationVisitsRepository extends TrekRepository<TourStationVisit
|
||||
|
||||
/** Drops the visits of stations a walk no longer has, on every day it is on. */
|
||||
async dropStationsNotIn(place_id: number, keep: readonly string[]): Promise<void> {
|
||||
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();
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Tour
|
||||
return sharedTestOrm(db).then((t) => t.repo(TourWaypoints));
|
||||
}
|
||||
|
||||
export function createTestTourStationVisitsRepo(db: Database.Database): Promise<TourStationVisitsRepository> {
|
||||
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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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': 'تم تعيين النقطة',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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é',
|
||||
|
||||
@@ -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': 'Το σημείο τοποθετήθηκε',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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': 'ポイントを配置しました',
|
||||
|
||||
@@ -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': '지점을 찍었습니다',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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': 'Точка поставлена',
|
||||
|
||||
@@ -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ý',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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': 'วางจุดแล้ว',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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': 'Точку поставлено',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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': '已放置點',
|
||||
|
||||
@@ -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': '已放置点',
|
||||
|
||||
@@ -23,9 +23,7 @@ export const tourVisitListResponseSchema = z.object({ visits: z.array(tourVisitS
|
||||
export type TourVisitListResponse = z.infer<typeof tourVisitListResponseSchema>;
|
||||
|
||||
/** 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<typeof tourVisitUpdateRequestSchema>;
|
||||
|
||||
/**
|
||||
@@ -34,7 +32,9 @@ export type TourVisitUpdateRequest = z.infer<typeof tourVisitUpdateRequestSchema
|
||||
* way between the stations is TREK's own, planned afresh.
|
||||
*/
|
||||
export const WALK_IMPORT_URL_MAX = 2048;
|
||||
export const walkImportPreviewRequestSchema = z.object({ url: z.string().trim().min(1).max(WALK_IMPORT_URL_MAX) }).strict();
|
||||
export const walkImportPreviewRequestSchema = z
|
||||
.object({ url: z.string().trim().min(1).max(WALK_IMPORT_URL_MAX) })
|
||||
.strict();
|
||||
export type WalkImportPreviewRequest = z.infer<typeof walkImportPreviewRequestSchema>;
|
||||
|
||||
/** A stop as the link names it; a stop without coordinates in the link is not guessed at. */
|
||||
|
||||
Reference in New Issue
Block a user