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:
jubnl
2026-04-15 06:07:26 +02:00
parent 801ffbfb7b
commit 875c91e5ff
22 changed files with 741 additions and 431 deletions
+9 -11
View File
@@ -889,21 +889,19 @@ const ru: Record<string, string> = {
// Places Sidebar
'places.addPlace': 'Добавить место/активность',
'places.importGpx': 'GPX',
'places.importKeyholeMarkup': 'KMZ / KML',
'places.importFile': 'Импортировать файл',
'places.sidebarDrop': 'Отпустите для импорта',
'places.importFileHint': 'Импортируйте файлы .gpx, .kml или .kmz из инструментов, таких как Google My Maps, Google Earth или GPS-трекер.',
'places.importFileDropHere': 'Нажмите для выбора файла или перетащите его сюда',
'places.importFileDropActive': 'Отпустите файл для выбора',
'places.importFileUnsupported': 'Неподдерживаемый тип файла. Используйте .gpx, .kml или .kmz.',
'places.importFileTooLarge': 'Файл слишком большой. Максимальный размер загрузки — {maxMb} MB.',
'places.importFileError': 'Ошибка импорта',
'places.importAllSkipped': 'Все места уже были в поездке.',
'places.gpxImported': '{count} мест импортировано из GPX',
'places.kmlKmzImported': '{count} мест импортировано из KMZ/KML',
'places.urlResolved': 'Место импортировано из URL',
'places.gpxError': 'Ошибка импорта GPX',
'places.importList': 'Импорт списка',
'places.kmlKmzImportError': 'Ошибка импорта KMZ/KML',
'places.kmlKmzInvalidType': 'Выберите файл .kml или .kmz.',
'places.kmlKmzTooLarge': 'Файл слишком большой. Максимальный размер загрузки — {maxMb} MB.',
'places.kmlKmzHint': 'Импортируйте файлы карт из инструментов, таких как Google My Maps и Google Earth.',
'places.kmlKmzSizeHint': 'Максимальный размер файла: {maxMb} MB',
'places.kmlKmzSelectFile': 'Выбрать файл',
'places.kmlKmzSelectedFile': 'Выбранный файл: {name}',
'places.kmlKmzSummaryTitle': 'Сводка импорта',
'places.kmlKmzSummaryValues': 'Placemarks: {total} • Импортировано: {created} • Пропущено: {skipped}',
'places.importGoogleList': 'Список Google',
'places.importNaverList': 'Список Naver',