← 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