mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 06:11:45 +00:00
feat(places): unified file import modal with drag-and-drop and deduplication
- Replace separate GPX and KML/KMZ import buttons with a single "Import file" modal accepting all three formats, with a drag-and-drop drop zone - Support dragging files directly onto the Places sidebar panel; overlay appears on hover and pre-loads the file into the modal on drop - Fix [object Object] description bug in KML imports caused by fast-xml-parser returning mixed-content nodes as objects; add stopNodes config and object guard in asTrimmedString - Fix CDATA sections leaking into descriptions (e.g. "text.]]>") by unwrapping CDATA markers before tag stripping - Add import deduplication across all import paths (GPX, KML/KMZ, Google list, Naver list): reimporting skips places already in the trip by name (case-insensitive) or by coordinates (within ~11 m tolerance), with intra-batch dedup so duplicate placemarks within the same file are also collapsed - Fix KML route returning 400 "No valid Placemarks found" when all placemarks were valid but deduplicated; 400 now only fires when the file contains zero placemarks - Show a warning toast "All places were already in the trip" instead of a misleading success toast when a reimport produces zero new places (GPX, KML/KMZ, Google list, Naver list) - Add 8 new i18n keys across all 14 locales; remove 11 keys made unused by the modal consolidation
This commit is contained in:
@@ -66,13 +66,13 @@ router.post('/import/gpx', authenticate, requireTripAccess, uploadMulter.single(
|
||||
const file = req.file as Express.Multer.File | undefined;
|
||||
if (!file) return res.status(400).json({ error: 'No file uploaded' });
|
||||
|
||||
const created = importGpx(tripId, file.buffer);
|
||||
if (!created) {
|
||||
const result = importGpx(tripId, file.buffer);
|
||||
if (!result) {
|
||||
return res.status(400).json({ error: 'No waypoints found in GPX file' });
|
||||
}
|
||||
|
||||
res.status(201).json({ places: created, count: created.length });
|
||||
for (const place of created) {
|
||||
res.status(201).json({ places: result.places, count: result.count, skipped: result.skipped });
|
||||
for (const place of result.places) {
|
||||
broadcast(tripId, 'place:created', { place }, req.headers['x-socket-id'] as string);
|
||||
}
|
||||
});
|
||||
@@ -89,7 +89,7 @@ router.post('/import/map', authenticate, requireTripAccess, uploadMulter.single(
|
||||
|
||||
try {
|
||||
const result = await importMapFile(tripId, file.buffer, file.originalname);
|
||||
if (result.count === 0) {
|
||||
if (result.summary?.totalPlacemarks === 0) {
|
||||
return res.status(400).json({ error: 'No valid Placemarks found in map file', summary: result.summary });
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ router.post('/import/google-list', authenticate, requireTripAccess, async (req:
|
||||
return res.status(result.status).json({ error: result.error });
|
||||
}
|
||||
|
||||
res.status(201).json({ places: result.places, count: result.places.length, listName: result.listName });
|
||||
res.status(201).json({ places: result.places, count: result.places.length, listName: result.listName, skipped: result.skipped });
|
||||
for (const place of result.places) {
|
||||
broadcast(tripId, 'place:created', { place }, req.headers['x-socket-id'] as string);
|
||||
}
|
||||
@@ -150,7 +150,7 @@ router.post('/import/naver-list', authenticate, requireTripAccess, async (req: R
|
||||
return res.status(result.status).json({ error: result.error });
|
||||
}
|
||||
|
||||
res.status(201).json({ places: result.places, count: result.places.length, listName: result.listName });
|
||||
res.status(201).json({ places: result.places, count: result.places.length, listName: result.listName, skipped: result.skipped });
|
||||
for (const place of result.places) {
|
||||
broadcast(tripId, 'place:created', { place }, req.headers['x-socket-id'] as string);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user