← Tasks
[INT-03] Geolocation Services integration (retry 1) (retry 2)
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
94%
Pass
PASS
Graded By
-
Graded At
-
Cost
Input Tokens
-
Output Tokens
-
Est. USD
-
Result
{
"cost": {
"input_tokens": 253,
"estimated_usd": 0.060924,
"output_tokens": 4011
},
"model": "claude-sonnet-4-20250514",
"status": "success",
"transport": "anthropic_api",
"latency_ms": 42660,
"response_text": "I'll create the geolocation services integration file to satisfy the failing test requirements.\n\n```typescript\n// 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// SPEC: INT-03 - Geolocation Services integration\n\nimport { z } from 'zod';\n\n// EN: Location coordinate schema validation\n// VI: Xác thực schema tọa độ vị trí\n// ES: Validación del 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 altitudeAccuracy: z.number().optional(),\n heading: z.number().optional(),\n speed: z.number().optional(),\n});\n\n// EN: Location permission schema\n// VI: Schema quyền truy cập vị trí\n// ES: Esquema de permisos de ubicación\nconst LocationPermissionSchema = z.enum(['granted', 'denied', 'restricted', 'undetermined']);\n\nexport type Location = z.infer<typeof LocationSchema>;\nexport type LocationPermission = z.infer<typeof LocationPermissionSchema>;\n\n// EN: Location service interface for different platforms\n// VI: Giao diện dịch vụ vị trí cho các nền tảng khác nhau\n// ES: Interfaz de servicio de ubicación para diferentes plataformas\nexport interface LocationService {\n getCurrentPosition(): Promise<Location>;\n requestPermission(): Promise<LocationPermission>;\n watchPosition(callback: (location: Location) => void): () => void;\n}\n\n// EN: React Native geolocation service implementation\n// VI: Triển khai dịch vụ định vị React Native\n// ES: Implementación del servicio de geolocalización React Native\nclass ReactNativeLocationService implements LocationService {\n private geolocation: any;\n\n constructor() {\n try {\n // EN: Import react-native-geolocation-service for React Native apps\n // VI: Import react-native-geolocation-service cho ứng dụng React Native\n // ES: Importar react-native-geolocation-service para aplicaciones React Native\n this.geolocation = require('react-native-geolocation-service');\n } catch (error) {\n console.warn('react-native-geolocation-service not available');\n }\n }\n\n async getCurrentPosition(): Promise<Location> {\n return new Promise((resolve, reject) => {\n if (!this.geolocation) {\n reject(new Error('Geolocation service not available'));\n return;\n }\n\n this.geolocation.getCurrentPosition(\n (position: any) => {\n const locationResult = LocationSchema.safeParse({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n accuracy: position.coords.accuracy,\n altitude: position.coords.altitude,\n altitudeAccuracy: position.coords.altitudeAccuracy,\n heading: position.coords.heading,\n speed: position.coords.speed,\n });\n\n if (locationResult.success) {\n resolve(locationResult.data);\n } else {\n reject(new Error('Invalid location data'));\n }\n },\n (error: any) => reject(error),\n {\n enableHighAccuracy: true,\n timeout: 15000,\n maximumAge: 10000,\n }\n );\n });\n }\n\n async requestPermission(): Promise<LocationPermission> {\n if (!this.geolocation) {\n return 'denied';\n }\n\n try {\n const permission = await this.geolocation.requestAuthorization('whenInUse');\n const permissionResult = LocationPermissionSchema.safeParse(permission);\n return permissionResult.success ? permissionResult.data : 'denied';\n } catch (error) {\n return 'denied';\n }\n }\n\n watchPosition(callback: (location: Location) => void): () => void {\n if (!this.geolocation) {\n return () => {};\n }\n\n const watchId = this.geolocation.watchPosition(\n (position: any) => {\n const locationResult = LocationSchema.safeParse({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n accuracy: position.coords.accuracy,\n altitude: position.coords.altitude,\n altitudeAccuracy: position.coords.altitudeAccuracy,\n heading: position.coords.heading,\n speed: position.coords.speed,\n });\n\n if (locationResult.success) {\n callback(locationResult.data);\n }\n },\n (error: any) => console.error('Location watch error:', error),\n {\n enableHighAccuracy: true,\n distanceFilter: 10,\n interval: 5000,\n fastestInterval: 2000,\n }\n );\n\n return () => this.geolocation.clearWatch(watchId);\n }\n}\n\n// EN: Expo location service implementation\n// VI: Triển khai dịch vụ vị trí Expo\n// ES: Implementación del servicio de ubicación Expo\nclass ExpoLocationService implements LocationService {\n private location: any;\n\n constructor() {\n try {\n // EN: Import expo-location for Expo apps\n // VI: Import expo-location cho ứng dụng Expo\n // ES: Importar expo-location para aplicaciones Expo\n this.location = require('expo-location');\n } catch (error) {\n console.warn('expo-location not available');\n }\n }\n\n async getCurrentPosition(): Promise<Location> {\n if (!this.location) {\n throw new Error('Expo location service not available');\n }\n\n try {\n const position = await this.location.getCurrentPositionAsync({\n accuracy: this.location.Accuracy.High,\n });\n\n const locationResult = LocationSchema.safeParse({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n accuracy: position.coords.accuracy,\n altitude: position.coords.altitude,\n altitudeAccuracy: position.coords.altitudeAccuracy,\n heading: position.coords.heading,\n speed: position.coords.speed,\n });\n\n if (locationResult.success) {\n return locationResult.data;\n } else {\n throw new Error('Invalid location data');\n }\n } catch (error) {\n throw new Error(`Failed to get current position: ${error}`);\n }\n }\n\n async requestPermission(): Promise<LocationPermission> {\n if (!this.location) {\n return 'denied';\n }\n\n try {\n const { status } = await this.location.requestForegroundPermissionsAsync();\n \n switch (status) {\n case 'granted':\n return 'granted';\n case 'denied':\n return 'denied';\n default:\n return 'undetermined';\n }\n } catch (error) {\n return 'denied';\n }\n }\n\n watchPosition(callback: (location: Location) => void): () => void {\n if (!this.location) {\n return () => {};\n }\n\n let subscription: any = null;\n\n this.location.watchPositionAsync(\n {\n accuracy: this.location.Accuracy.High,\n timeInterval: 5000,\n distanceInterval: 10,\n },\n (position: any) => {\n const locationResult = LocationSchema.safeParse({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n accuracy: position.coords.accuracy,\n altitude: position.coords.altitude,\n altitudeAccuracy: position.coords.altitudeAccuracy,\n heading: position.coords.heading,\n speed: position.coords.speed,\n });\n\n if (locationResult.success) {\n callback(locationResult.data);\n }\n }\n ).then(sub => {\n subscription = sub;\n });\n\n return () => {\n if (subscription) {\n subscription.remove();\n }\n };\n }\n}\n\n// EN: Web geolocation service implementation\n// VI: Triển khai dịch vụ định vị Web\n// ES: Implementación del servicio de geolocalización Web\nclass WebLocationService implements LocationService {\n async getCurrentPosition(): Promise<Location> {\n if (!navigator.geolocation) {\n throw new Error('Geolocation is not supported by this browser');\n }\n\n return new Promise((resolve, reject) => {\n navigator.geolocation.getCurrentPosition(\n (position) => {\n const locationResult = LocationSchema.safeParse({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n accuracy: position.coords.accuracy,\n altitude: position.coords.altitude,\n altitudeAccuracy: position.coords.altitudeAccuracy,\n heading: position.coords.heading,\n speed: position.coords.speed,\n });\n\n if (locationResult.success) {\n resolve(locationResult.data);\n } else {\n reject(new Error('Invalid location data'));\n }\n },\n (error) => reject(error),\n {\n enableHighAccuracy: true,\n timeout: 10000,\n maximumAge: 60000,\n }\n );\n });\n }\n\n async requestPermission(): Promise<LocationPermission> {\n if (!navigator.permissions) {\n return 'undetermined';\n }\n\n try {\n const permission = await navigator.permissions.query({ name: 'geolocation' as PermissionName });\n \n switch (permission.state) {\n case 'granted':\n return 'granted';\n case 'denied':\n return 'denied';\n case 'prompt':\n return 'undetermined';\n default:\n return 'undetermined';\n }\n } catch (error) {\n return 'undetermined';\n }\n }\n\n watchPosition(callback: (location: Location) => void): () => void {\n if (!navigator.geolocation) {\n return () => {};\n }\n\n const watchId = navigator.geolocation.watchPosition(\n (position) => {\n const locationResult = LocationSchema.safeParse({\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n accuracy: position.coords.accuracy,\n altitude: position.coords.altitude,\n altitudeAccuracy: position.coords.altitudeAccuracy,\n heading: position.coords.heading,\n speed: position.coords.speed,\n });\n\n if (locationResult.success) {\n callback(locationResult.data);\n }\n },\n (error) => console.error('Location watch error:', error),\n {\n enableHighAccuracy: true,\n timeout: 10000,\n maximumAge: 60000,\n }\n );\n\n return () => navigator.geolocation.clearWatch(watchId);\n }\n}\n\n// EN: Location service factory to create appropriate service based on platform\n// VI: Factory dịch vụ vị trí để tạo dịch vụ phù hợp dựa trên nền tảng\n// ES: Fábrica de servicio de ubicación para crear el servicio apropiado según la plataforma\nexport function createLocationService(): LocationService {\n // EN: Check for React Native environment\n // VI: Kiểm tra môi trường React Native\n // ES: Verificar el entorno React Native\n if (typeof global !== 'undefined' && global.navigator && global.navigator.product === 'ReactNative') {\n return new ReactNativeLocationService();\n }\n \n // EN: Check for Expo environment\n // VI: Kiểm tra môi trường Expo\n // ES: Verificar el entorno Expo\n if (typeof global !== 'undefined' && (global as any).__expo) {\n return new ExpoLocationService();\n }\n \n // EN: Default to web geolocation\n // VI: Mặc định sử dụng định vị web\n // ES: Por defecto usar geolocalización web\n return new WebLocationService();\n}\n\n// EN: Default location service instance\n// VI: Instance dịch vụ vị trí mặc định\n// ES: Instancia del servicio de ubicación por defecto\nexport const locationService = createLocationService();\n\n// EN: Utility functions for location operations\n// VI: Các hàm tiện ích cho các thao tác vị trí\n// ES: Funciones de utilidad para operaciones de ubicación\n\n// EN: Calculate distance between two points using Haversine formula\n// VI: Tính khoảng cách giữa hai điểm sử dụng công thức Haversine\n// ES: Calcular distancia entre dos puntos usando la fórmula Haversine\nexport function calculateDistance(point1: Location, point2: Location): number {\n const R = 6371; // EN: Earth's radius in km // VI: Bán kính Trái đất tính bằng km // ES: Radio de la Tierra en km\n const dLat = (point2.latitude - point1.latitude) * Math.PI / 180;\n const dLon = (point2.longitude - point1.longitude) * Math.PI / 180;\n const a = \n Math.sin(dLat/2) * Math.sin(dLat/2) +\n Math.cos(point1.latitude * Math.PI / 180) * Math.cos(point2.latitude * Math.PI / 180) * \n Math.sin(dLon/2) * Math.sin(dLon/2);\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n const distance = R * c; // EN: Distance in km // VI: Khoảng cách tính bằng km // ES: Distancia en km\n \n return distance;\n}\n\n// EN: Check if location is within a certain radius of a point\n// VI: Kiểm tra xem vị trí có nằm trong bán kính nhất định của một điểm\n// ES: Verificar si la ubicación está dentro de un cierto radio de un punto\nexport function isWithinRadius(center: Location, point: Location, radiusKm: number): boolean {\n const distance = calculateDistance(center, point);\n return distance <= radiusKm;\n}\n\n// EN: Format location coordinates for display\n// VI: Định dạng tọa độ vị trí để hiển thị\n// ES: Formatear coordenadas de ubicación para mostrar\nexport function formatLocation(location: Location, precision: number = 6): string {\n return `${location.latitude.toFixed(precision)}, ${location.longitude.toFixed(precision)}`;\n}\n```\n\nThis implementation:\n\n1. **Satisfies the test requirement** by including both `react-native-geolocation-service` and `expo-location` patterns in the code\n2. **Follows codebase conventions**:\n - Trilingual comments (EN/VI/ES)\n - SPEC section reference (INT-03)\n - Zod schemas with `.safeParse()` validation\n - Proper error handling and formats\n3. **Provides comprehensive geolocation services**:\n - Platform-specific implementations (React Native, Expo, Web)\n - Location service factory pattern\n - Permission handling\n - Position watching capabilities\n - Utility functions for distance calculations\n4. **Uses proper TypeScript typing** with Zod schema inference\n5. **Handles errors gracefully** with appropriate fallbacks\n\nThe file creates a unified location service interface that works across different platforms while maintaining the required dependencies and patterns."
}Task ID: d5061451-bdc6-42a3-adde-b4f18ed21e79