feat(import): selective GPX/KML element import and performance improvements

Add type-selector UI in the file import modal letting users choose which
GPX elements (waypoints, routes, tracks) or KML/KMZ elements (points,
paths) to import. KML LineString placemarks are now imported as path
places with route_geometry.

Performance improvements:
- Extract MemoPlaceRow with React.memo and contentVisibility:auto to cut
  unnecessary re-renders in PlacesSidebar
- Add weatherQueue to cap concurrent weather fetches at 3
- Replace sequential per-place deletes with a single bulkDelete API call
  (new DELETE /places/bulk endpoint + deletePlacesMany service)
- Memoize atlas/photo/weather service calls to avoid redundant requests
- Add multi-select mode to PlacesSidebar for bulk operations

Add large GPX/KML/KMZ fixtures for integration/perf testing and two
profiler analysis scripts under scripts/.
This commit is contained in:
jubnl
2026-04-18 01:28:37 +02:00
parent 9a31fcac7b
commit 6a718fccea
45 changed files with 22471 additions and 285 deletions
+18
View File
@@ -10,6 +10,9 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'common.add': 'Hinzufügen',
'common.loading': 'Laden...',
'common.import': 'Importieren',
'common.select': 'Auswählen',
'common.selectAll': 'Alle auswählen',
'common.deselectAll': 'Alle abwählen',
'common.error': 'Fehler',
'common.unknownError': 'Unbekannter Fehler',
'common.tooManyAttempts': 'Zu viele Versuche. Bitte versuchen Sie es später erneut.',
@@ -880,6 +883,8 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'trip.toast.reservationAdded': 'Reservierung hinzugefügt',
'trip.toast.deleted': 'Gelöscht',
'trip.confirm.deletePlace': 'Möchtest du diesen Ort wirklich löschen?',
'trip.confirm.deletePlaces': '{count} Orte löschen?',
'trip.toast.placesDeleted': '{count} Orte gelöscht',
// Day Plan Sidebar
'dayplan.emptyDay': 'Keine Orte für diesen Tag geplant',
@@ -924,6 +929,17 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'places.importFileError': 'Import fehlgeschlagen',
'places.importAllSkipped': 'Alle Orte waren bereits in der Reise.',
'places.gpxImported': '{count} Orte aus GPX importiert',
'places.gpxImportTypes': 'Was soll importiert werden?',
'places.gpxImportWaypoints': 'Wegpunkte',
'places.gpxImportRoutes': 'Routen',
'places.gpxImportTracks': 'Tracks (mit Streckenverlauf)',
'places.gpxImportNoneSelected': 'Wähle mindestens einen Typ zum Importieren.',
'places.kmlImportTypes': 'Was möchtest du importieren?',
'places.kmlImportPoints': 'Punkte (Placemarks)',
'places.kmlImportPaths': 'Pfade (LineStrings)',
'places.kmlImportNoneSelected': 'Wähle mindestens einen Typ aus.',
'places.selectionCount': '{count} ausgewählt',
'places.deleteSelected': 'Auswahl löschen',
'places.kmlKmzImported': '{count} Orte aus KMZ/KML importiert',
'places.urlResolved': 'Ort aus URL importiert',
'places.importList': 'Listenimport',
@@ -940,6 +956,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'places.assignToDay': 'Zu welchem Tag hinzufügen?',
'places.all': 'Alle',
'places.unplanned': 'Ungeplant',
'places.filterTracks': 'Tracks',
'places.search': 'Orte suchen...',
'places.allCategories': 'Alle Kategorien',
'places.categoriesSelected': 'Kategorien',
@@ -1703,6 +1720,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
'undo.reorder': 'Orte neu sortiert',
'undo.optimize': 'Route optimiert',
'undo.deletePlace': 'Ort gelöscht',
'undo.deletePlaces': 'Orte gelöscht',
'undo.moveDay': 'Ort zu anderem Tag verschoben',
'undo.lock': 'Ortssperre umgeschaltet',
'undo.importGpx': 'GPX-Import',