Skip to content

Commit 8f020ae

Browse files
committed
added ride estimate api and ui
1 parent 8bc1109 commit 8f020ae

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

app/pages/rides/[id].vue

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
const { data: session } = await authClient.useSession(useFetch)
1010
const { data: ride, status, refresh: refreshRide } = await useFetch(`/api/get/rides/byId/${id}`)
11+
const { data: estimate } = await useFetch(`/api/get/rides/estimate/${id}`)
1112
1213
const isEditModalOpen = ref(false)
1314
const isDeleteModalOpen = ref(false)
@@ -273,6 +274,17 @@
273274
</div>
274275
</div>
275276

277+
<div v-if="estimate && !estimate.error" class="flex gap-6 rounded-lg bg-gray-50 p-4 dark:bg-gray-800/50">
278+
<div>
279+
<p class="text-sm text-gray-500">Est. Duration</p>
280+
<p class="font-medium">{{ estimate.duration }}</p>
281+
</div>
282+
<div>
283+
<p class="text-sm text-gray-500">Distance</p>
284+
<p class="font-medium">{{ estimate.distance }}</p>
285+
</div>
286+
</div>
287+
276288
<div class="aspect-video w-full overflow-hidden rounded-lg border">
277289
<iframe
278290
v-if="mapUrl && useRuntimeConfig().public.googleMapsApiKey"
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { prisma } from '../../../../utils/prisma'
2+
3+
interface EstimateResponse {
4+
duration: string | null
5+
distance: string | null
6+
durationValue: number | null
7+
distanceValue: number | null
8+
error: string | null
9+
}
10+
11+
export default defineEventHandler(async (event): Promise<EstimateResponse> => {
12+
const id = getRouterParam(event, 'id')
13+
const config = useRuntimeConfig()
14+
15+
if (!id) {
16+
throw createError({
17+
statusCode: 400,
18+
statusMessage: 'ID is required',
19+
})
20+
}
21+
22+
const ride = await prisma.ride.findUnique({
23+
where: { id },
24+
select: { pickupDisplay: true, dropoffDisplay: true }
25+
})
26+
27+
if (!ride) {
28+
throw createError({
29+
statusCode: 404,
30+
statusMessage: 'Ride not found',
31+
})
32+
}
33+
34+
const apiKey = config.public.googleMapsApiKey
35+
36+
if (!apiKey) {
37+
return {
38+
duration: null,
39+
distance: null,
40+
durationValue: null,
41+
distanceValue: null,
42+
error: 'Maps API Key not configured'
43+
}
44+
}
45+
46+
const origin = encodeURIComponent(ride.pickupDisplay)
47+
const destination = encodeURIComponent(ride.dropoffDisplay)
48+
49+
try {
50+
const url = `https://maps.googleapis.com/maps/api/directions/json?origin=${origin}&destination=${destination}&key=${apiKey}`
51+
const response: any = await $fetch(url)
52+
53+
if (response.status === 'OK' && response.routes.length > 0 && response.routes[0].legs.length > 0) {
54+
const leg = response.routes[0].legs[0]
55+
return {
56+
duration: leg.duration.text,
57+
distance: leg.distance.text,
58+
durationValue: leg.duration.value, // seconds
59+
distanceValue: leg.distance.value, // meters
60+
error: null
61+
}
62+
} else {
63+
return {
64+
duration: null,
65+
distance: null,
66+
durationValue: null,
67+
distanceValue: null,
68+
error: response.status || 'No route found'
69+
}
70+
}
71+
} catch (error) {
72+
console.error('Maps API error:', error)
73+
return {
74+
duration: null,
75+
distance: null,
76+
durationValue: null,
77+
distanceValue: null,
78+
error: 'Failed to fetch estimate'
79+
}
80+
}
81+
})

0 commit comments

Comments
 (0)