diff --git a/client/app/(private)/home/routes/(from)/(to)/page.tsx b/client/app/(private)/home/routes/(from)/(to)/page.tsx index 56444fa..4f64c4e 100644 --- a/client/app/(private)/home/routes/(from)/(to)/page.tsx +++ b/client/app/(private)/home/routes/(from)/(to)/page.tsx @@ -245,18 +245,14 @@ const RouteContent = () => { coordinates: [destination.lng, destination.lat], }, }, - routes: [ - { - distance: routes[selectedRouteIndex].distance, - duration: routes[selectedRouteIndex].duration, - routeGeometry: routes[selectedRouteIndex].geometry, - lastComputedScore: - routes[selectedRouteIndex].aqiScore || - Math.floor(Math.random() * 100), - lastComputedAt: new Date(), - travelMode: selectedMode, - }, - ], + routes: routes.map((route) => ({ + distance: route.distance / 1000, + duration: route.duration / 60, + routeGeometry: route.geometry, + lastComputedScore: route.aqiScore || Math.floor(Math.random() * 100), + lastComputedAt: new Date(), + travelMode: selectedMode, + })), isFavorite: false, }; @@ -304,6 +300,8 @@ const RouteContent = () => { onModeChange={handleModeChange} routeName={routeName} onRouteNameChange={setRouteName} + onSaveRoute={saveRoute} + canSave={!isLoading && !error && routes.length > 0} /> { /> - - {/* Save Route Button - Bottom Right */} - {!isLoading && !error && routes.length > 0 && ( - - )} ); diff --git a/client/app/(private)/profile/page.tsx b/client/app/(private)/profile/page.tsx index 9622b54..e7429af 100644 --- a/client/app/(private)/profile/page.tsx +++ b/client/app/(private)/profile/page.tsx @@ -1,3 +1,5 @@ +import { Suspense } from "react"; + import { cookies } from "next/headers"; import { redirect } from "next/navigation"; @@ -42,7 +44,23 @@ export default async function ProfilePage() {
- + +
+
+ {[1, 2, 3].map((i) => ( +
+ ))} +
+
+ } + > + +
diff --git a/client/app/layout.tsx b/client/app/layout.tsx index 9a08148..02479ef 100644 --- a/client/app/layout.tsx +++ b/client/app/layout.tsx @@ -1,7 +1,7 @@ import type { Metadata } from "next"; import { Outfit } from "next/font/google"; -import { toast, Toaster } from "sonner"; +import { Toaster } from "sonner"; import "./globals.css"; diff --git a/client/components/home/HomeMap.tsx b/client/components/home/HomeMap.tsx index 2c54cc6..2ed5b4f 100644 --- a/client/components/home/HomeMap.tsx +++ b/client/components/home/HomeMap.tsx @@ -318,14 +318,15 @@ export default function HomeMap({ className }: HomeMapProps) { initMap(); + const markersInstance = markersRef.current; // Capture ref value + return () => { isCancelled = true; cleanupRef.current(); // Safe Cleanup - const markers = markersRef.current; - if (markers.source) markers.source.remove(); - if (markers.dest) markers.dest.remove(); + if (markersInstance.source) markersInstance.source.remove(); + if (markersInstance.dest) markersInstance.dest.remove(); mapRef.current = null; }; diff --git a/client/components/profile/ProfileCard.tsx b/client/components/profile/ProfileCard.tsx index 8ea2559..a544d58 100644 --- a/client/components/profile/ProfileCard.tsx +++ b/client/components/profile/ProfileCard.tsx @@ -26,6 +26,8 @@ export default function ProfileCard({ user }: { user: UserData }) {
{user.picture ? ( {user.name} + +
+ ); +} diff --git a/client/components/profile/SavedRoutes.tsx b/client/components/profile/SavedRoutes.tsx index 54e0c28..c34436f 100644 --- a/client/components/profile/SavedRoutes.tsx +++ b/client/components/profile/SavedRoutes.tsx @@ -1,75 +1,121 @@ -import { Clock, MapPin, Navigation } from "lucide-react"; +import { cookies } from "next/headers"; +import Link from "next/link"; -const savedRoutes = [ - { - name: "Home to Office", - from: "Connaught Place", - to: "Cyber City, Gurugram", - aqiScore: 62, - lastUsed: "Today", - }, - { - name: "Morning Jog Route", - from: "Lodhi Garden Gate 1", - to: "Lodhi Garden Gate 3", - aqiScore: 45, - lastUsed: "Yesterday", - }, - { - name: "Weekend Market", - from: "Sarojini Nagar Metro", - to: "Sarojini Nagar Market", - aqiScore: 89, - lastUsed: "3 days ago", - }, -]; +import { Clock, Inbox, MapPin } from "lucide-react"; + +import type { ISavedRoute } from "../saved-routes/types"; +import SavedRouteItemClient from "./SavedRouteItemClient"; function getAqiBadge(aqi: number) { - if (aqi <= 50) return { label: "Good", color: "bg-green-100 text-green-700" }; + if (aqi <= 50) + return { label: "Good", color: "bg-emerald-100 text-emerald-700" }; if (aqi <= 100) return { label: "Moderate", color: "bg-yellow-100 text-yellow-700" }; return { label: "Poor", color: "bg-red-100 text-red-700" }; } -export default function SavedRoutes() { +async function getTopRoutes(): Promise { + const cookieStore = await cookies(); + const refreshToken = cookieStore.get("refreshToken"); + + if (!refreshToken) return []; + + try { + const res = await fetch( + `${process.env.NEXT_PUBLIC_BACKEND_URL}/api/v1/saved-routes`, + { + headers: { + Cookie: `refreshToken=${refreshToken.value}`, + }, + next: { revalidate: 0 }, // Ensure fresh data + } + ); + + if (!res.ok) return []; + const data = await res.json(); + return data.success && data.routes ? data.routes.slice(0, 3) : []; + } catch (error) { + console.error("Failed to fetch routes on server:", error); + return []; + } +} + +export default async function SavedRoutes() { + const routes = await getTopRoutes(); + + if (routes.length === 0) { + return ( +
+
+
+ +
+

+ No Routes Found +

+

+ You haven't saved any routes yet. Start exploring to find the + cleanest paths for your journey. +

+ + Find a Route + +
+
+ ); + } + return (

Saved Routes

- - {savedRoutes.length} routes + + Showing Top {routes.length}
- {savedRoutes.map((route) => { - const badge = getAqiBadge(route.aqiScore); + {routes.map((route) => { + const aqi = route.routes?.[0]?.lastComputedScore ?? 0; + const badge = getAqiBadge(aqi); + + const date = new Date(route.updatedAt); + const lastUsed = date.toLocaleDateString("en-IN", { + day: "numeric", + month: "short", + }); + return ( -
-
- -
+
-

{route.name}

-

+

+ {route.name || "Untitled Route"} +

+

- {route.from} → {route.to} + {route.from.address.split(",")[0]} →{" "} + {route.to.address.split(",")[0]}

-
+
- AQI {route.aqiScore} · {badge.label} + AQI {aqi} · {badge.label} - + - {route.lastUsed} + {lastUsed}
-
+ ); })}
diff --git a/client/components/routes/RouteDiscoveryPanel.tsx b/client/components/routes/RouteDiscoveryPanel.tsx index 6c4fe76..120755e 100644 --- a/client/components/routes/RouteDiscoveryPanel.tsx +++ b/client/components/routes/RouteDiscoveryPanel.tsx @@ -1,13 +1,12 @@ "use client"; -import { useState } from "react"; - import { useRouter } from "next/navigation"; import { - Activity, Bike, + Bookmark, Car, + ChevronLeft, CircleDot, Info, MapPin, @@ -24,6 +23,8 @@ type RouteDiscoveryPanelProps = { onModeChange: (mode: TravelMode) => void; routeName: string; onRouteNameChange: (name: string) => void; + onSaveRoute: () => void; + canSave: boolean; }; export default function RouteDiscoveryPanel({ @@ -33,6 +34,8 @@ export default function RouteDiscoveryPanel({ onModeChange, routeName, onRouteNameChange, + onSaveRoute, + canSave, }: RouteDiscoveryPanelProps) { const router = useRouter(); @@ -43,13 +46,24 @@ export default function RouteDiscoveryPanel({ return (