Add self-hosted auth, admin API, and owner web dashboard
- Replace Clerk with self-hosted JWT auth (register/login/verify, bcrypt passwords, Gmail OTP with console fallback) - Add lib/db.ts pg pool + transaction helpers; seed script migrates legacy Clerk-era schema (drop clerk_id, enforce UUID ids and unique email) - Add owner-gated admin API: stats, users, drivers CRUD, rides - Add dashboard/ Vite React owner dashboard (login, overview, users, fleet, rides) with dev-server proxy to avoid Expo CORS middleware - Add scripts/set-owner.mjs for role management
This commit is contained in:
+24
-10
@@ -1,3 +1,4 @@
|
||||
import { calculateFare } from "@/lib/pricing";
|
||||
import type { Driver, MarkerData } from "@/types/type";
|
||||
|
||||
const directionsAPI = process.env.EXPO_PUBLIC_GOOGLE_API_KEY;
|
||||
@@ -37,11 +38,12 @@ export const calculateRegion = ({
|
||||
destinationLongitude?: number | null;
|
||||
}) => {
|
||||
if (!userLatitude || !userLongitude) {
|
||||
// Default to Beirut, Lebanon.
|
||||
return {
|
||||
latitude: 37.78825,
|
||||
longitude: -122.4324,
|
||||
latitudeDelta: 0.01,
|
||||
longitudeDelta: 0.01,
|
||||
latitude: 33.8938,
|
||||
longitude: 35.5018,
|
||||
latitudeDelta: 0.09,
|
||||
longitudeDelta: 0.09,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -100,20 +102,32 @@ export const calculateDriverTimes = async ({
|
||||
`https://maps.googleapis.com/maps/api/directions/json?origin=${marker.latitude},${marker.longitude}&destination=${userLatitude},${userLongitude}&key=${directionsAPI}`,
|
||||
);
|
||||
const dataToUser = await responseToUser.json();
|
||||
const timeToUser = dataToUser.routes[0].legs[0].duration.value; // Time in seconds
|
||||
|
||||
const responseToDestination = await fetch(
|
||||
`https://maps.googleapis.com/maps/api/directions/json?origin=${userLatitude},${userLongitude}&destination=${destinationLatitude},${destinationLongitude}&key=${directionsAPI}`,
|
||||
);
|
||||
const dataToDestination = await responseToDestination.json();
|
||||
|
||||
const timeToDestination =
|
||||
dataToDestination.routes[0].legs[0].duration.value; // Time in seconds
|
||||
// Google returns no routes when a leg is unreachable (ZERO_RESULTS).
|
||||
const legToUser = dataToUser.routes?.[0]?.legs?.[0];
|
||||
const legToDestination = dataToDestination.routes?.[0]?.legs?.[0];
|
||||
if (!legToUser || !legToDestination) {
|
||||
return { ...marker, time: 0, price: "0.00" };
|
||||
}
|
||||
|
||||
const totalTime = (timeToUser + timeToDestination) / 60; // Total time in minutes
|
||||
const price = (totalTime * 0.5).toFixed(2); // Calculate price based on time
|
||||
const timeToUser = legToUser.duration.value; // Pickup ETA in seconds
|
||||
const timeToDestination = legToDestination.duration.value; // Trip duration in seconds
|
||||
|
||||
return { ...marker, time: totalTime, price };
|
||||
// The rider pays for the trip leg only (distance + duration) —
|
||||
// never for the driver's approach.
|
||||
const price = calculateFare({
|
||||
distanceMeters: legToDestination.distance.value,
|
||||
durationSeconds: timeToDestination,
|
||||
});
|
||||
|
||||
const totalTripTime = (timeToUser + timeToDestination) / 60; // Minutes until drop-off
|
||||
|
||||
return { ...marker, time: totalTripTime, price };
|
||||
});
|
||||
|
||||
return await Promise.all(timesPromises);
|
||||
|
||||
Reference in New Issue
Block a user