FOUNDRY
C8 Platform
← 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