mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-22 06:41:46 +00:00
3e9626fce9
* feat(places): enrich list-imported places via the Places API (#886) Google/Naver list imports only carry a name and coordinates, so the places open as bare pins — the Maps tab jumps to coordinates, with no photo, address or open/closed. Add an opt-in "Enrich places via Google" toggle to the list-import dialog, shown only when a Google Maps key is configured. When enabled, after the (fast, unchanged) import the server runs a background pass that re-resolves each place by name — biased to and validated against the imported coordinates so a common-name search cannot overwrite the wrong place — and fills the empty address/website/phone/photo columns plus the resolved google_place_id, pushing each row over the live sync. Opening hours and the proper Maps link then work on demand from the stored id. Enrichment only fills empty fields, runs detached so a long list never blocks the import, and no-ops when no key is configured. * fix(places): use the ToggleSwitch component for the enrich toggle Match the rest of the app — the import-enrichment opt-in used a raw checkbox; swap it for the shared ToggleSwitch (text left, switch right) like the settings toggles.
84 lines
3.0 KiB
TypeScript
84 lines
3.0 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { broadcast } from '../../websocket';
|
|
import { canAccessTrip } from '../../db/database';
|
|
import { checkPermission } from '../../services/permissions';
|
|
import type { User } from '../../types';
|
|
import * as svc from '../../services/placeService';
|
|
import { onPlaceCreated, onPlaceUpdated, onPlaceDeleted } from '../../services/journeyService';
|
|
|
|
type Trip = { user_id: number };
|
|
|
|
/**
|
|
* Thin Nest wrapper around the existing place service. Trip access mirrors the
|
|
* requireTripAccess middleware (canAccessTrip); mutations use 'place_edit'. The
|
|
* SQL, the GPX/map/list importers and the journey hooks reuse the legacy code
|
|
* unchanged.
|
|
*/
|
|
@Injectable()
|
|
export class PlacesService {
|
|
verifyTripAccess(tripId: string, userId: number) {
|
|
return canAccessTrip(Number(tripId), userId) as Trip | null | undefined;
|
|
}
|
|
|
|
canEdit(trip: Trip, user: User): boolean {
|
|
return checkPermission('place_edit', user.role, trip.user_id, user.id, trip.user_id !== user.id);
|
|
}
|
|
|
|
broadcast(tripId: string, event: string, payload: Record<string, unknown>, socketId: string | undefined): void {
|
|
broadcast(tripId, event, payload, socketId);
|
|
}
|
|
|
|
list(tripId: string, filters: { search?: string; category?: string; tag?: string }) {
|
|
return svc.listPlaces(tripId, filters);
|
|
}
|
|
|
|
get(tripId: string, id: string) {
|
|
return svc.getPlace(tripId, id);
|
|
}
|
|
|
|
create(tripId: string, data: Parameters<typeof svc.createPlace>[1]) {
|
|
return svc.createPlace(tripId, data);
|
|
}
|
|
|
|
update(tripId: string, id: string, data: Parameters<typeof svc.updatePlace>[2]) {
|
|
return svc.updatePlace(tripId, id, data);
|
|
}
|
|
|
|
remove(tripId: string, id: string): boolean {
|
|
return svc.deletePlace(tripId, id);
|
|
}
|
|
|
|
removeMany(tripId: string, ids: number[]): number[] {
|
|
return svc.deletePlacesMany(tripId, ids);
|
|
}
|
|
|
|
importGpx(
|
|
tripId: string,
|
|
buffer: Buffer,
|
|
opts: { importWaypoints: boolean; importRoutes: boolean; importTracks: boolean; defaultName?: string },
|
|
) {
|
|
return svc.importGpx(tripId, buffer, opts);
|
|
}
|
|
|
|
importMapFile(tripId: string, buffer: Buffer, filename: string, opts: svc.KmlImportOptions) {
|
|
return svc.importMapFile(tripId, buffer, filename, opts);
|
|
}
|
|
|
|
importGoogleList(tripId: string, url: string, opts?: Parameters<typeof svc.importGoogleList>[2]) {
|
|
return svc.importGoogleList(tripId, url, opts);
|
|
}
|
|
|
|
importNaverList(tripId: string, url: string, opts?: Parameters<typeof svc.importNaverList>[2]) {
|
|
return svc.importNaverList(tripId, url, opts);
|
|
}
|
|
|
|
searchImage(tripId: string, id: string, userId: number) {
|
|
return svc.searchPlaceImage(tripId, id, userId);
|
|
}
|
|
|
|
// Journey hooks — non-fatal, mirroring the route's try/catch wrappers.
|
|
onCreated(tripId: string, placeId: number): void { try { onPlaceCreated(Number(tripId), placeId); } catch { /* non-fatal */ } }
|
|
onUpdated(placeId: number): void { try { onPlaceUpdated(placeId); } catch { /* non-fatal */ } }
|
|
onDeleted(placeId: number): void { try { onPlaceDeleted(placeId); } catch { /* non-fatal */ } }
|
|
}
|