+
- Route Preview
+ {isPredicting ? (
+ Calculating route...
+ ) : (
+ routeError ?
+ Route not found :
+ Route Preview
+ )}
{from && to ? (
{from.name ?? 'Start'} → {to.name ?? 'End'}
@@ -163,7 +276,15 @@ export default function LeafletMap({ from, to, animateKey }: LeafletMapProps) {
Select locations to preview
)}
-
+
+
+ {isRouteLoading && (
+
+
+
+ )}
+
+
)
}
\ No newline at end of file
diff --git a/frontend/src/components/LocationSearch.tsx b/frontend/src/components/LocationSearch.tsx
index bb335f8..047ed07 100644
--- a/frontend/src/components/LocationSearch.tsx
+++ b/frontend/src/components/LocationSearch.tsx
@@ -16,7 +16,7 @@ interface LocationSearchProps {
label: string;
value: string;
onChange: (val: string) => void;
- onLocationSelect: (location: Location) => void;
+ onLocationSelect: (location: Location | null) => void;
selectedLocation?: Location | null;
placeholder?: string;
className?: string;
@@ -46,7 +46,13 @@ const SF_LOCATIONS: Location[] = [
{ id: "sf_mission", name: "Mission District, SF", lat: 37.7599, lon: -122.4148 },
{ id: "sf_soma", name: "SoMa, SF", lat: 37.7749, lon: -122.4194 },
{ id: "sf_marina", name: "Marina District, SF", lat: 37.8024, lon: -122.4368 },
- { id: "sf_sfo_airport", name: "SFO Airport, SF", lat: 37.6213, lon: -122.3790 },
+//? Wrong data possibly { id: "sf_sfo_airport", name: "SFO Airport, SF", lat: 37.6213, lon: -122.3790 },
+{ id: "sf_sfo_airport", name: "SFO Airport, SF", lat: 37.6163, lon: -122.3863 }, //! had coorinates of runway which is not publicaly accessible
+
+
+
+
+
{ id: "sf_oakland_airport", name: "Oakland Airport, SF", lat: 37.7126, lon: -122.2196 },
];
@@ -109,7 +115,7 @@ export function LocationSearch({
// If user clears the input, clear the selected location
if (!newValue.trim()) {
- onLocationSelect(null as any);
+ onLocationSelect(null);
}
};
@@ -174,7 +180,7 @@ export function LocationSearch({
className="absolute right-1 top-1/2 h-6 w-6 -translate-y-1/2 p-0"
onClick={() => {
onChange('');
- onLocationSelect(null as any);
+ onLocationSelect(null);
}}
>
×
@@ -200,7 +206,7 @@ export function LocationSearch({
{location.name}
- {location.lat.toFixed(4)}, {location.lon.toFixed(4)}
+ {location.lat.toFixed(8)}, {location.lon.toFixed(8)}
diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx
index 269b2bc..12b26c0 100644
--- a/frontend/src/pages/Home.tsx
+++ b/frontend/src/pages/Home.tsx
@@ -1,11 +1,11 @@
import { useState, useEffect, useRef } from "react";
import { LocationSearch } from "@/components/LocationSearch";
import LeafletMap from "@/components/LeafletMap";
-import { DateTimePicker } from "@/components/DateTimePicker";
+import { DateTimePicker } from "@/components/DateTimePicker/DateTimePicker";
import { Button } from "@/components/ui/button";
import { ThemeToggle } from "@/components/ui/theme-toggle";
import { predictTravelTime } from "@/lib/api";
-import { Clock, MapPin, Car, AlertTriangle } from "lucide-react";
+import { Clock, MapPin, Car, AlertTriangle ,Loader2} from "lucide-react";
import Footer from "@/components/Footer";
import { motion, AnimatePresence } from "framer-motion";
@@ -49,10 +49,12 @@ export default function Home() {
const [travelDate, setTravelDate] = useState
(new Date());
const [predicted, setPredicted] = useState(null);
const [animKey, setAnimKey] = useState(0);
+
const [currentCity, setCurrentCity] = useState<"new_york" | "san_francisco">(
"new_york"
);
const [isLoading, setIsLoading] = useState(false);
+ const [isPredicting, setIsPredicting] = useState(false);
const [warning, setWarning] = useState(""); // Add this line
// Update city when location changes
@@ -107,6 +109,7 @@ export default function Home() {
}
setIsLoading(true);
+ setIsPredicting(true);
const isMobile = window.innerWidth <= 768;
// Validate that both locations are within the same city
@@ -120,6 +123,7 @@ export default function Home() {
"Cross-city travel is not supported. Please select locations within the same city (New York or San Francisco)"
);
setIsLoading(false);
+ setIsPredicting(false);
return;
}
@@ -131,6 +135,7 @@ export default function Home() {
city: currentCity,
});
+ console.log('Prediction API response:', response);
if (typeof response.minutes === "number" && isFinite(response.minutes)) {
setPredicted(response.minutes);
if (isMobile) {
@@ -138,23 +143,22 @@ export default function Home() {
} else {
setAnimKey((k) => k + 1);
}
- setIsLoading(false);
return;
}
} catch (error) {
console.error("Prediction API error:", error);
+ // Clear previous prediction if API fails, so fallback is used or it's reset
+ setPredicted(null);
+ } finally {
+ // Fallback calculation if prediction is still null after API call
+ if (predicted === null) {
+ const km = haversineKm(fromLocation, toLocation);
+ const minutes = estimateMinutes(km, travelDate);
+ setPredicted(minutes);
+ }
+ setIsLoading(false);
+ setIsPredicting(false);
}
-
- // Fallback calculation
- const km = haversineKm(fromLocation, toLocation);
- const minutes = estimateMinutes(km, travelDate);
- setPredicted(minutes);
- if (isMobile) {
- setTimeout(() => setAnimKey((k) => k + 1), 900);
- } else {
- setAnimKey((k) => k + 1);
- }
- setIsLoading(false);
};
const resultRef = useRef(null);
@@ -347,6 +351,7 @@ export default function Home() {
from={fromLocation}
to={toLocation}
animateKey={`${animKey}-${fromLocation?.id}-${toLocation?.id}`}
+ isPredicting={isPredicting}
/>
@@ -459,7 +464,14 @@ export default function Home() {
disabled={!fromLocation || !toLocation || !travelDate || isLoading}
className="h-12 w-full rounded-lg bg-primary text-primary-foreground shadow-soft transition hover:brightness-95 disabled:cursor-not-allowed disabled:opacity-50"
>
- Predict Travel Time
+ {isLoading ? (
+ <>
+