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 fr: Record<string, string> = {
'common.add': 'Ajouter',
'common.loading': 'Chargement…',
'common.import': 'Importer',
'common.select': 'Sélectionner',
'common.selectAll': 'Tout sélectionner',
'common.deselectAll': 'Tout désélectionner',
'common.error': 'Erreur',
'common.unknownError': 'Erreur inconnue',
'common.tooManyAttempts': 'Trop de tentatives. Veuillez réessayer plus tard.',
@@ -876,6 +879,8 @@ const fr: Record<string, string> = {
'trip.toast.reservationAdded': 'Réservation ajoutée',
'trip.toast.deleted': 'Supprimé',
'trip.confirm.deletePlace': 'Voulez-vous vraiment supprimer ce lieu ?',
'trip.confirm.deletePlaces': 'Supprimer {count} lieux?',
'trip.toast.placesDeleted': '{count} lieux supprimés',
// Day Plan Sidebar
'dayplan.emptyDay': 'Aucun lieu prévu pour ce jour',
@@ -920,6 +925,17 @@ const fr: Record<string, string> = {
'places.importFileError': 'Importation échouée',
'places.importAllSkipped': 'Tous les lieux étaient déjà dans le voyage.',
'places.gpxImported': '{count} lieux importés depuis GPX',
'places.gpxImportTypes': 'Que voulez-vous importer?',
'places.gpxImportWaypoints': 'Points de passage',
'places.gpxImportRoutes': 'Itinéraires',
'places.gpxImportTracks': 'Traces (avec géométrie)',
'places.gpxImportNoneSelected': 'Sélectionnez au moins un type à importer.',
'places.kmlImportTypes': 'Que souhaitez-vous importer ?',
'places.kmlImportPoints': 'Points (Placemarks)',
'places.kmlImportPaths': 'Chemins (LineStrings)',
'places.kmlImportNoneSelected': 'Sélectionnez au moins un type.',
'places.selectionCount': '{count} sélectionné(s)',
'places.deleteSelected': 'Supprimer la sélection',
'places.kmlKmzImported': '{count} lieux importés depuis KMZ/KML',
'places.urlResolved': 'Lieu importé depuis l\'URL',
'places.importList': 'Import de liste',
@@ -936,6 +952,7 @@ const fr: Record<string, string> = {
'places.assignToDay': 'Ajouter à quel jour ?',
'places.all': 'Tous',
'places.unplanned': 'Non planifiés',
'places.filterTracks': 'Traces',
'places.search': 'Rechercher des lieux…',
'places.allCategories': 'Toutes les catégories',
'places.categoriesSelected': 'catégories',
@@ -1699,6 +1716,7 @@ const fr: Record<string, string> = {
'undo.reorder': 'Lieux réorganisés',
'undo.optimize': 'Itinéraire optimisé',
'undo.deletePlace': 'Lieu supprimé',
'undo.deletePlaces': 'Lieux supprimés',
'undo.moveDay': 'Lieu déplacé vers un autre jour',
'undo.lock': 'Verrouillage du lieu modifié',
'undo.importGpx': 'Import GPX',