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
@@ -14,6 +14,9 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'common.add': 'إضافة',
'common.loading': 'جارٍ التحميل...',
'common.import': 'استيراد',
'common.select': 'تحديد',
'common.selectAll': 'تحديد الكل',
'common.deselectAll': 'إلغاء تحديد الكل',
'common.error': 'خطأ',
'common.unknownError': 'خطأ غير معروف',
'common.tooManyAttempts': 'محاولات كثيرة جدًا. يرجى المحاولة لاحقًا.',
@@ -879,6 +882,8 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'trip.toast.reservationAdded': 'تمت إضافة الحجز',
'trip.toast.deleted': 'تم الحذف',
'trip.confirm.deletePlace': 'هل تريد حذف هذا المكان؟',
'trip.confirm.deletePlaces': 'حذف {count} أماكن؟',
'trip.toast.placesDeleted': 'تم حذف {count} أماكن',
// Day Plan Sidebar
'dayplan.emptyDay': 'لا توجد أماكن مخططة لهذا اليوم',
@@ -923,6 +928,17 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'places.importFileError': 'فشل الاستيراد',
'places.importAllSkipped': 'جميع الأماكن موجودة بالفعل في الرحلة.',
'places.gpxImported': 'تم استيراد {count} مكان من GPX',
'places.gpxImportTypes': 'ما الذي تريد استيراده؟',
'places.gpxImportWaypoints': 'نقاط الطريق',
'places.gpxImportRoutes': 'المسارات',
'places.gpxImportTracks': 'المسارات (مع هندسة الطريق)',
'places.gpxImportNoneSelected': 'اختر نوعاً واحداً على الأقل للاستيراد.',
'places.kmlImportTypes': 'ما الذي تريد استيراده؟',
'places.kmlImportPoints': 'نقاط (Placemarks)',
'places.kmlImportPaths': 'مسارات (LineStrings)',
'places.kmlImportNoneSelected': 'اختر نوعًا واحدًا على الأقل.',
'places.selectionCount': '{count} محدد',
'places.deleteSelected': 'حذف المحدد',
'places.kmlKmzImported': 'تم استيراد {count} مكان من KMZ/KML',
'places.urlResolved': 'تم استيراد المكان من الرابط',
'places.importList': 'استيراد قائمة',
@@ -939,6 +955,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'places.assignToDay': 'إلى أي يوم تريد الإضافة؟',
'places.all': 'الكل',
'places.unplanned': 'غير مخطط',
'places.filterTracks': 'المسارات',
'places.search': 'ابحث عن أماكن...',
'places.allCategories': 'كل الفئات',
'places.categoriesSelected': 'فئات',
@@ -1754,6 +1771,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
'undo.reorder': 'تمت إعادة ترتيب الأماكن',
'undo.optimize': 'تم تحسين المسار',
'undo.deletePlace': 'تم حذف المكان',
'undo.deletePlaces': 'تم حذف الأماكن',
'undo.moveDay': 'تم نقل المكان إلى يوم آخر',
'undo.lock': 'تم تبديل قفل المكان',
'undo.importGpx': 'استيراد GPX',