FOUNDRY
C8 Platform
← Tasks

[INT-03] Geolocation Services integration (retry 1)

completedcode_genP0

Description

## INT-03: Geolocation Services integration Section: Integration | Milestone: M2 | Owner: None ### Failing Tests - **geolocation_library** (grep): Pattern `react-native-geolocation-service|expo-location` in `src/lib/location.ts` (need 1+ matches) ### Acceptance Criteria - File matching `src/lib/location.ts` must contain pattern `react-native-geolocation-service|expo-location` ### Codebase Conventions - Trilingual comments: // EN: ... // VI: ... // ES: ... - SPEC section refs in comments - Zod at module level with .safeParse() - (supabase as any) with eslint-disable - NextResponse.json() responses - Error format: { error: 'message' } ### Dependencies - UI-03: FAIL - UI-04: FAIL - UI-11: FAIL
Assignee
-
Claimed By
Cloud Lu → Claude Sonnet
Created
1d ago
Completed
1d ago

Quality

Overall
89%
Pass
PASS
Graded By
-
Graded At
-

Cost

Input Tokens
-
Output Tokens
-
Est. USD
-

Result

{
  "cost": {
    "input_tokens": 248,
    "estimated_usd": 0.052929,
    "output_tokens": 3479
  },
  "model": "claude-sonnet-4-20250514",
  "status": "success",
  "transport": "anthropic_api",
  "latency_ms": 41799,
  "response_text": "I'll create the geolocation services integration by implementing the location library with React Native geolocation service.\n\n<blovk type=\"text/file\" path=\"src/lib/location.ts\">\n// EN: Geolocation services integration for location-based features\n// VI: Tích hợp dịch vụ định vị địa lý cho các tính năng dựa trên vị trí\n// ES: Integración de servicios de geolocalización para funciones basadas en ubicación\n\nimport { z } from 'zod';\nimport Geolocation from 'react-native-geolocation-service';\nimport { Platform, PermissionsAndroid } from 'react-native';\n\n// EN: Location coordinates schema validation\n// VI: Xác thực schema tọa độ vị trí\n// ES: Validación de esquema de coordenadas de ubicación\nconst LocationSchema = z.object({\n  latitude: z.number().min(-90).max(90),\n  longitude: z.number().min(-180).max(180),\n  accuracy: z.number().optional(),\n  altitude: z.number().optional(),\n  heading: z.number().optional(),\n  speed: z.number().optional(),\n});\n\nconst LocationPermissionSchema = z.enum([\n  'granted',\n  'denied',\n  'restricted',\n  'undetermined'\n]);\n\nexport type Location = z.infer<typeof LocationSchema>;\nexport type LocationPermission = z.infer<typeof LocationPermissionSchema>;\n\n// EN: Location service configuration options\n// VI: Tùy chọn cấu hình dịch vụ định vị\n// ES: Opciones de configuración del servicio de ubicación\ninterface LocationOptions {\n  enableHighAccuracy?: boolean;\n  timeout?: number;\n  maximumAge?: number;\n  distanceFilter?: number;\n  interval?: number;\n  fastestInterval?: number;\n}\n\n// EN: Default location options for optimal performance\n// VI: Tùy chọn vị trí mặc định cho hiệu suất tối ưu\n// ES: Opciones de ubicación predeterminadas para rendimiento óptimo\nconst DEFAULT_OPTIONS: LocationOptions = {\n  enableHighAccuracy: true,\n  timeout: 15000,\n  maximumAge: 10000,\n  distanceFilter: 10,\n  interval: 5000,\n  fastestInterval: 2000,\n};\n\nclass LocationService {\n  private watchId: number | null = null;\n  private lastKnownLocation: Location | null = null;\n\n  // EN: Request location permissions based on platform\n  // VI: Yêu cầu quyền truy cập vị trí dựa trên nền tảng\n  // ES: Solicitar permisos de ubicación según la plataforma\n  async requestPermissions(): Promise<LocationPermission> {\n    try {\n      if (Platform.OS === 'android') {\n        const granted = await PermissionsAndroid.request(\n          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,\n          {\n            title: 'Location Permission',\n            message: 'This app needs access to your location for delivery tracking.',\n            buttonNeutral: 'Ask Me Later',\n            buttonNegative: 'Cancel',\n            buttonPositive: 'OK',\n          }\n        );\n        \n        const permission = granted === PermissionsAndroid.RESULTS.GRANTED \n          ? 'granted' \n          : 'denied';\n        \n        const validationResult = LocationPermissionSchema.safeParse(permission);\n        return validationResult.success ? validationResult.data : 'denied';\n      }\n\n      // EN: iOS permission handling through Geolocation service\n      // VI: Xử lý quyền iOS thông qua dịch vụ Geolocation\n      // ES: Manejo de permisos de iOS a través del servicio de Geolocalización\n      return new Promise((resolve) => {\n        Geolocation.requestAuthorization('whenInUse')\n          .then((result) => {\n            const permission = result === 'granted' ? 'granted' : 'denied';\n            const validationResult = LocationPermissionSchema.safeParse(permission);\n            resolve(validationResult.success ? validationResult.data : 'denied');\n          })\n          .catch(() => resolve('denied'));\n      });\n    } catch (error) {\n      console.error('Permission request failed:', error);\n      return 'denied';\n    }\n  }\n\n  // EN: Get current location with error handling and validation\n  // VI: Lấy vị trí hiện tại với xử lý lỗi và xác thực\n  // ES: Obtener ubicación actual con manejo de errores y validación\n  async getCurrentLocation(options: LocationOptions = {}): Promise<Location | null> {\n    const mergedOptions = { ...DEFAULT_OPTIONS, ...options };\n    \n    return new Promise((resolve, reject) => {\n      Geolocation.getCurrentPosition(\n        (position) => {\n          const locationData = {\n            latitude: position.coords.latitude,\n            longitude: position.coords.longitude,\n            accuracy: position.coords.accuracy,\n            altitude: position.coords.altitude,\n            heading: position.coords.heading,\n            speed: position.coords.speed,\n          };\n\n          const validationResult = LocationSchema.safeParse(locationData);\n          if (validationResult.success) {\n            this.lastKnownLocation = validationResult.data;\n            resolve(validationResult.data);\n          } else {\n            reject(new Error('Invalid location data received'));\n          }\n        },\n        (error) => {\n          console.error('Location error:', error);\n          resolve(null);\n        },\n        mergedOptions\n      );\n    });\n  }\n\n  // EN: Start watching location changes with continuous updates\n  // VI: Bắt đầu theo dõi thay đổi vị trí với cập nhật liên tục\n  // ES: Comenzar a observar cambios de ubicación con actualizaciones continuas\n  watchLocation(\n    onLocationChange: (location: Location) => void,\n    onError: (error: string) => void,\n    options: LocationOptions = {}\n  ): void {\n    const mergedOptions = { ...DEFAULT_OPTIONS, ...options };\n\n    this.watchId = Geolocation.watchPosition(\n      (position) => {\n        const locationData = {\n          latitude: position.coords.latitude,\n          longitude: position.coords.longitude,\n          accuracy: position.coords.accuracy,\n          altitude: position.coords.altitude,\n          heading: position.coords.heading,\n          speed: position.coords.speed,\n        };\n\n        const validationResult = LocationSchema.safeParse(locationData);\n        if (validationResult.success) {\n          this.lastKnownLocation = validationResult.data;\n          onLocationChange(validationResult.data);\n        } else {\n          onError('Invalid location data received');\n        }\n      },\n      (error) => {\n        console.error('Watch location error:', error);\n        onError(`Location watch failed: ${error.message}`);\n      },\n      mergedOptions\n    );\n  }\n\n  // EN: Stop watching location changes and cleanup\n  // VI: Dừng theo dõi thay đổi vị trí và dọn dẹp\n  // ES: Detener observación de cambios de ubicación y limpieza\n  stopWatchingLocation(): void {\n    if (this.watchId !== null) {\n      Geolocation.clearWatch(this.watchId);\n      this.watchId = null;\n    }\n  }\n\n  // EN: Calculate distance between two coordinates using Haversine formula\n  // VI: Tính khoảng cách giữa hai tọa độ sử dụng công thức Haversine\n  // ES: Calcular distancia entre dos coordenadas usando fórmula de Haversine\n  calculateDistance(\n    location1: Pick<Location, 'latitude' | 'longitude'>,\n    location2: Pick<Location, 'latitude' | 'longitude'>\n  ): number {\n    const R = 6371; // EN: Earth's radius in kilometers / VI: Bán kính Trái đất tính bằng km / ES: Radio de la Tierra en kilómetros\n    const dLat = this.toRadians(location2.latitude - location1.latitude);\n    const dLon = this.toRadians(location2.longitude - location1.longitude);\n    \n    const a = \n      Math.sin(dLat / 2) * Math.sin(dLat / 2) +\n      Math.cos(this.toRadians(location1.latitude)) * \n      Math.cos(this.toRadians(location2.latitude)) * \n      Math.sin(dLon / 2) * Math.sin(dLon / 2);\n    \n    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n    return R * c;\n  }\n\n  // EN: Get last known location without requesting new position\n  // VI: Lấy vị trí được biết cuối cùng mà không yêu cầu vị trí mới\n  // ES: Obtener última ubicación conocida sin solicitar nueva posición\n  getLastKnownLocation(): Location | null {\n    return this.lastKnownLocation;\n  }\n\n  // EN: Check if location services are available on device\n  // VI: Kiểm tra xem dịch vụ định vị có khả dụng trên thiết bị không\n  // ES: Verificar si los servicios de ubicación están disponibles en el dispositivo\n  async isLocationEnabled(): Promise<boolean> {\n    try {\n      // EN: Check if GPS/Location services are enabled\n      // VI: Kiểm tra xem GPS/Dịch vụ định vị có được bật không\n      // ES: Verificar si GPS/Servicios de ubicación están habilitados\n      const enabled = await new Promise<boolean>((resolve) => {\n        Geolocation.getCurrentPosition(\n          () => resolve(true),\n          (error) => {\n            // EN: Error code 2 means location services are disabled\n            // VI: Mã lỗi 2 có nghĩa là dịch vụ định vị bị tắt\n            // ES: El código de error 2 significa que los servicios de ubicación están deshabilitados\n            resolve(error.code !== 2);\n          },\n          { timeout: 1000, maximumAge: 0 }\n        );\n      });\n      \n      return enabled;\n    } catch (error) {\n      console.error('Location availability check failed:', error);\n      return false;\n    }\n  }\n\n  private toRadians(degrees: number): number {\n    return degrees * (Math.PI / 180);\n  }\n}\n\n// EN: Export singleton instance for global usage\n// VI: Xuất instance singleton để sử dụng toàn cầu\n// ES: Exportar instancia singleton para uso global\nexport const locationService = new LocationService();\n\n// EN: Utility functions for common location operations\n// VI: Các hàm tiện ích cho các thao tác vị trí phổ biến\n// ES: Funciones de utilidad para operaciones de ubicación comunes\nexport const LocationUtils = {\n  // EN: Format coordinates for display\n  // VI: Định dạng tọa độ để hiển thị\n  // ES: Formatear coordenadas para mostrar\n  formatCoordinates(location: Location): string {\n    return `${location.latitude.toFixed(6)}, ${location.longitude.toFixed(6)}`;\n  },\n\n  // EN: Check if location is within a specific radius\n  // VI: Kiểm tra xem vị trí có nằm trong bán kính cụ thể không\n  // ES: Verificar si la ubicación está dentro de un radio específico\n  isWithinRadius(\n    center: Pick<Location, 'latitude' | 'longitude'>,\n    target: Pick<Location, 'latitude' | 'longitude'>,\n    radiusKm: number\n  ): boolean {\n    const distance = locationService.calculateDistance(center, target);\n    return distance <= radiusKm;\n  },\n\n  // EN: Validate location data structure\n  // VI: Xác thực cấu trúc dữ liệu vị trí\n  // ES: Validar estructura de datos de ubicación\n  validateLocation(data: unknown): Location | null {\n    const result = LocationSchema.safeParse(data);\n    return result.success ? result.data : null;\n  }\n};\n\nexport default locationService;\n</blovk>\n\nThe geolocation services integration has been successfully implemented with the following features:\n\n## Key Features Implemented:\n\n1. **React Native Geolocation Service Integration**:\n   - Uses `react-native-geolocation-service` for cross-platform location services\n   - Handles both Android and iOS platform-specific permissions\n\n2. **Location Data Validation**:\n   - Zod schema validation for location coordinates\n   - Safe parsing of location data with proper error handling\n\n3. **Permission Management**:\n   - Platform-specific permission requests\n   - Proper handling of Android PermissionsAndroid API\n   - iOS authorization handling\n\n4. **Location Services**:\n   - Current location retrieval with options\n   - Location watching with continuous updates\n   - Distance calculation using Haversine formula\n   - Last known location caching\n\n5. **Utility Functions**:\n   - Coordinate formatting for display\n   - Radius-based location checking\n   - Location data validation helpers\n\n6. **Error Handling**:\n   - Comprehensive error handling for location failures\n   - Validation of location data before usage\n   - Fallback mechanisms for service unavailability\n\nThe implementation follows the codebase conventions with trilingual comments, Zod validation at module level with `.safeParse()`, and proper error handling patterns. The service provides a complete location management solution for the delivery tracking application."
}
Task ID: 557d8b3b-1164-4421-94d7-d2c16a2a4981