Waseel: driver app, dispatch, POI suggestions, map fixes
This commit is contained in:
+65
-41
@@ -6,6 +6,7 @@ import { CustomButton } from "@/components/custom-button";
|
||||
import { RideLayout } from "@/components/ride-layout";
|
||||
import { SERVICES } from "@/constants/services";
|
||||
import { ApiError, fetchAPI } from "@/lib/fetch";
|
||||
import { useT } from "@/lib/i18n";
|
||||
import { calculateTripFare } from "@/lib/map";
|
||||
import { formatLBP } from "@/lib/pricing";
|
||||
import { requestRide } from "@/lib/request-ride";
|
||||
@@ -38,6 +39,7 @@ const ConfirmRide = () => {
|
||||
} = useLocationStore();
|
||||
const { service: storeService, setService } = useServiceStore();
|
||||
const { user } = useSession();
|
||||
const t = useT();
|
||||
|
||||
const service = params.service ?? storeService;
|
||||
const selected = SERVICES.find((s) => s.id === service) ?? SERVICES[0];
|
||||
@@ -154,11 +156,17 @@ const ConfirmRide = () => {
|
||||
|
||||
const request = async () => {
|
||||
if (!userLatitude || !userLongitude || !destinationLatitude || !destinationLongitude) {
|
||||
Alert.alert("Missing route", "Please set a pickup and destination first.");
|
||||
Alert.alert(
|
||||
t("confirmRide.alertMissingRouteTitle"),
|
||||
t("confirmRide.alertMissingRouteBody"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!estimate) {
|
||||
Alert.alert("No estimate", "We couldn't estimate this fare. Please try again.");
|
||||
Alert.alert(
|
||||
t("confirmRide.alertNoEstimateTitle"),
|
||||
t("confirmRide.alertNoEstimateBody"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,8 +202,8 @@ const ConfirmRide = () => {
|
||||
const msg =
|
||||
err instanceof ApiError
|
||||
? err.message
|
||||
: "Something went wrong while booking your ride. Please try again.";
|
||||
Alert.alert("Error", msg);
|
||||
: t("confirmRide.alertErrorFallback");
|
||||
Alert.alert(t("confirmRide.alertErrorTitle"), msg);
|
||||
} finally {
|
||||
setProcessing(false);
|
||||
}
|
||||
@@ -203,11 +211,11 @@ const ConfirmRide = () => {
|
||||
|
||||
if (method === "card") {
|
||||
Alert.alert(
|
||||
"Pay by card",
|
||||
`Your card will be charged $${estimate.fare}.`,
|
||||
t("confirmRide.alertPayCardTitle"),
|
||||
t("confirmRide.alertPayCardBody", { fare: estimate.fare }),
|
||||
[
|
||||
{ text: "Cancel", style: "cancel" },
|
||||
{ text: "Continue", onPress: () => void doRequest() },
|
||||
{ text: t("common.cancel"), style: "cancel" },
|
||||
{ text: t("common.continue"), onPress: () => void doRequest() },
|
||||
],
|
||||
);
|
||||
} else {
|
||||
@@ -216,39 +224,53 @@ const ConfirmRide = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<RideLayout title="Request Ride" snapPoints={["60%", "88%"]}>
|
||||
<Text className="text-xl font-JakartaSemiBold mb-1">Your trip</Text>
|
||||
<RideLayout title={t("confirmRide.title")} snapPoints={["60%", "88%"]}>
|
||||
<Text className="text-xl font-JakartaSemiBold mb-1 text-black dark:text-white">
|
||||
{t("confirmRide.yourTrip")}
|
||||
</Text>
|
||||
|
||||
<View className="flex-row items-center gap-x-2 mb-1">
|
||||
<Text className="text-general-200 text-xs">Pickup</Text>
|
||||
<Text className="text-general-200 dark:text-neutral-400 text-xs">
|
||||
{t("confirmRide.pickup")}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className="font-JakartaMedium mb-3" numberOfLines={1}>
|
||||
<Text className="font-JakartaMedium mb-3 text-black dark:text-white" numberOfLines={1}>
|
||||
{userAddress}
|
||||
</Text>
|
||||
|
||||
<View className="flex-row items-center gap-x-2 mb-1">
|
||||
<Text className="text-general-200 text-xs">Destination</Text>
|
||||
<Text className="text-general-200 dark:text-neutral-400 text-xs">
|
||||
{t("confirmRide.destination")}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className="font-JakartaMedium mb-4" numberOfLines={1}>
|
||||
<Text className="font-JakartaMedium mb-4 text-black dark:text-white" numberOfLines={1}>
|
||||
{destinationAddress}
|
||||
</Text>
|
||||
|
||||
<View className="flex-row items-center justify-between bg-general-500 rounded-2xl p-4 mb-4">
|
||||
<View className="flex-row items-center justify-between bg-general-500 dark:bg-neutral-950 rounded-2xl p-4 mb-4">
|
||||
<View>
|
||||
<Text className="text-general-200 text-xs font-JakartaMedium">
|
||||
{selected.label} · {selected.tagline}
|
||||
<Text className="text-general-200 dark:text-neutral-400 text-xs font-JakartaMedium">
|
||||
{t(selected.labelKey)} · {t(selected.taglineKey)}
|
||||
</Text>
|
||||
<Text className="text-general-200 text-xs mt-1">
|
||||
Trip time {estimate ? formatTime(estimate.durationSeconds / 60) : "…"}
|
||||
<Text className="text-general-200 dark:text-neutral-400 text-xs mt-1">
|
||||
{t("confirmRide.tripTime", {
|
||||
time: estimate ? formatTime(estimate.durationSeconds / 60) : "…",
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
<View className="items-end">
|
||||
<Text className="text-2xl font-JakartaExtraBold">
|
||||
{estimating ? "…" : estimate ? `$${estimate.fare}` : "—"}
|
||||
<Text className="text-2xl font-JakartaExtraBold text-black dark:text-white">
|
||||
{estimating
|
||||
? "…"
|
||||
: estimate
|
||||
? t("confirmRide.fareDisplay", { fare: estimate.fare })
|
||||
: "—"}
|
||||
</Text>
|
||||
{estimate ? (
|
||||
<Text className="text-xs text-general-200">
|
||||
≈ {formatLBP(parseFloat(estimate.fare))}
|
||||
<Text className="text-xs text-general-200 dark:text-neutral-400">
|
||||
{t("confirmRide.lbpEstimate", {
|
||||
lbp: formatLBP(parseFloat(estimate.fare)),
|
||||
})}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
@@ -256,50 +278,52 @@ const ConfirmRide = () => {
|
||||
|
||||
<Text
|
||||
className={`text-base font-JakartaMedium mb-2 ${
|
||||
driversOnline === 0 ? "text-rose-500" : "text-general-200"
|
||||
driversOnline === 0
|
||||
? "text-rose-500"
|
||||
: "text-general-200 dark:text-neutral-400"
|
||||
}`}
|
||||
>
|
||||
{driversOnline === 0
|
||||
? `No ${selected.label} drivers online right now`
|
||||
? t("confirmRide.noDrivers", { service: t(selected.labelKey) })
|
||||
: nearestEta == null
|
||||
? "Finding drivers nearby…"
|
||||
: `Nearest driver ≈ ${nearestEta} min away`}
|
||||
? t("confirmRide.findingDrivers")
|
||||
: t("confirmRide.nearestDriver", { eta: nearestEta })}
|
||||
</Text>
|
||||
|
||||
<Text className="text-lg font-JakartaSemiBold mt-2 mb-2">
|
||||
Payment Method
|
||||
<Text className="text-lg font-JakartaSemiBold mt-2 mb-2 text-black dark:text-white">
|
||||
{t("confirmRide.paymentMethod")}
|
||||
</Text>
|
||||
<View className="flex-row gap-x-3 mb-2">
|
||||
<TouchableOpacity
|
||||
onPress={() => setMethod("cash")}
|
||||
className={`flex-1 items-center py-3 rounded-xl border ${
|
||||
method === "cash"
|
||||
? "bg-general-600 border-primary-500"
|
||||
: "bg-white border-general-700"
|
||||
? "bg-general-600 dark:bg-primary-500/20 border-primary-500"
|
||||
: "bg-white dark:bg-neutral-900 border-general-700 dark:border-neutral-700"
|
||||
}`}
|
||||
>
|
||||
<Text
|
||||
className={`font-JakartaMedium ${
|
||||
method === "cash" ? "text-white" : "text-black"
|
||||
method === "cash" ? "text-white" : "text-black dark:text-white"
|
||||
}`}
|
||||
>
|
||||
💵 Cash
|
||||
{t("confirmRide.cash")}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onPress={() => setMethod("card")}
|
||||
className={`flex-1 items-center py-3 rounded-xl border ${
|
||||
method === "card"
|
||||
? "bg-general-600 border-primary-500"
|
||||
: "bg-white border-general-700"
|
||||
? "bg-general-600 dark:bg-primary-500/20 border-primary-500"
|
||||
: "bg-white dark:bg-neutral-900 border-general-700 dark:border-neutral-700"
|
||||
}`}
|
||||
>
|
||||
<Text
|
||||
className={`font-JakartaMedium ${
|
||||
method === "card" ? "text-white" : "text-black"
|
||||
method === "card" ? "text-white" : "text-black dark:text-white"
|
||||
}`}
|
||||
>
|
||||
💳 Card
|
||||
{t("confirmRide.card")}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
@@ -307,12 +331,12 @@ const ConfirmRide = () => {
|
||||
<CustomButton
|
||||
title={
|
||||
processing
|
||||
? "Requesting…"
|
||||
? t("confirmRide.requesting")
|
||||
: driversOnline === 0
|
||||
? "No drivers online"
|
||||
? t("confirmRide.noDriversOnline")
|
||||
: method === "cash"
|
||||
? "Request Ride · Pay cash to driver"
|
||||
: "Request Ride · Pay by card"
|
||||
? t("confirmRide.requestCash")
|
||||
: t("confirmRide.requestCard")
|
||||
}
|
||||
className="mt-4"
|
||||
onPress={request}
|
||||
|
||||
Reference in New Issue
Block a user