📱 EN ROUTE STATUS - Developer Documentation
API Endpoints
// Get job details
GET /api/agentownjobs/{id}
// Mark as arrived
POST /api/agentownjobs/{id}/arrived
{
"timestamp": "2024-12-05T11:15:00Z",
"location": {
"lat": 51.5074,
"lng": -0.1278
}
}
// Update location during travel
POST /api/agentownjobs/{id}/location
{
"lat": 51.5074,
"lng": -0.1278,
"timestamp": "2024-12-05T11:00:00Z"
}
Travel Time Calculations
// Swift (iOS)
let travelStartTime = job.travelStartTime ?? Date()
let elapsedMinutes = Int(Date().timeIntervalSince(travelStartTime) / 60)
travelTimeLabel.text = "\(elapsedMinutes) min"
// Update every minute
Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
updateTravelTime()
}
// Kotlin (Android)
val travelStartTime = job.travelStartTime ?: System.currentTimeMillis()
val elapsedMinutes = (System.currentTimeMillis() - travelStartTime) / 60000
travelTimeText.text = "$elapsedMinutes min"
// Update with Handler
handler.postDelayed({
updateTravelTime()
}, 60000)
// Dart (Flutter)
final travelStartTime = job.travelStartTime ?? DateTime.now();
final elapsedMinutes = DateTime.now().difference(travelStartTime).inMinutes;
setState(() {
travelTime = '$elapsedMinutes min';
});
// Update with Timer
Timer.periodic(Duration(minutes: 1), (timer) {
updateTravelTime();
});
ETA Calculation
// Swift
let estimatedDuration = job.estimatedTravelMinutes ?? 30
let eta = travelStartTime.addingTimeInterval(Double(estimatedDuration * 60))
let formatter = DateFormatter()
formatter.timeStyle = .short
etaLabel.text = formatter.string(from: eta)
// Kotlin
val estimatedDuration = job.estimatedTravelMinutes ?: 30
val eta = travelStartTime + (estimatedDuration * 60000)
val formatter = SimpleDateFormat("h:mm a", Locale.getDefault())
etaText.text = formatter.format(Date(eta))
// Dart
final estimatedDuration = job.estimatedTravelMinutes ?? 30;
final eta = travelStartTime.add(Duration(minutes: estimatedDuration));
final formattedEta = DateFormat('h:mm a').format(eta);
Location Tracking
// Swift - Request location updates
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
locationManager.distanceFilter = 100 // Update every 100 meters
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
// Send to server
APIClient.updateLocation(jobId: job.id,
lat: location.coordinate.latitude,
lng: location.coordinate.longitude)
}
// Android - Location updates
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED) {
locationRequest = LocationRequest.create().apply {
interval = 60000 // 1 minute
fastestInterval = 30000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
smallestDisplacement = 100f // 100 meters
}
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
// Flutter - Location tracking
final location = Location();
if (await location.hasPermission() == PermissionStatus.granted) {
location.onLocationChanged.listen((LocationData currentLocation) {
if (currentLocation.latitude != null &&
currentLocation.longitude != null) {
// Send to server
updateJobLocation(
jobId: job.id,
lat: currentLocation.latitude!,
lng: currentLocation.longitude!
);
}
});
}
Status Transition
// When "I've Arrived" is clicked
async function markArrived() {
try {
const response = await fetch(`/api/agentownjobs/${jobId}/arrived`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
timestamp: new Date().toISOString(),
location: currentLocation
})
});
if (response.ok) {
// Job status changes from "EnRoute" to "InProgress"
navigateToInProgress();
}
} catch (error) {
showError('Failed to mark arrival');
}
}
Required API Response Fields
{
"id": "guid",
"jobReference": "GSC-2024-1847",
"jobType": "Meter Reading",
"status": "EnRoute",
"priority": "Urgent",
"customerName": "Mr. John Smith",
"customerPhone": "+44 20 7123 4567",
"propertyAddress": "123 High Street, London, SW1A 1AA",
"scheduledTime": "11:00 AM - 12:00 PM",
"specialInstructions": "Side gate access. Ring doorbell twice.",
"travelStartTime": "2024-12-05T10:45:00Z",
"estimatedTravelMinutes": 30,
"distance": 3.2,
"distanceUnit": "miles",
"trafficCondition": "Light",
"location": {
"lat": 51.5074,
"lng": -0.1278
}
}