From 3cce76b09bac49b075a24c41f1399ad3b3fd44d8 Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Sun, 15 Jun 2025 17:10:39 +0530 Subject: [PATCH 01/10] some update --- src/app/(auth)/actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(auth)/actions.ts b/src/app/(auth)/actions.ts index a51e24d..9b3316e 100644 --- a/src/app/(auth)/actions.ts +++ b/src/app/(auth)/actions.ts @@ -20,6 +20,6 @@ export async function logout() { path: "/api/auth/refresh", maxAge: 0, }); - const keycloakLogoutUrl = `${process.env.NEXT_PUBLIC_KEYCLOAK_LOGOUT_URL || "http://localhost:8084/realms/kong/protocol/openid-connect/logout"}?client_id=kong-oidc&post_logout_redirect_uri=${process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI || "http://localhost:3000/login"}`; + const keycloakLogoutUrl = `${process.env.NEXT_PUBLIC_KEYCLOAK_LOGOUT_URL || "http://localhost:8084/realms/kong/protocol/openid-connect/logout"}?client_id=kong-oidc&post_logout_redirect_uri=${encodeURIComponent(process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI || "http://localhost:3000/login")}`; return redirect(keycloakLogoutUrl); } From 266f07d7806d44bc8239bf5961c6eef0f1fa119f Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Sun, 15 Jun 2025 17:21:03 +0530 Subject: [PATCH 02/10] chages logout logic --- src/app/(auth)/actions.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/app/(auth)/actions.ts b/src/app/(auth)/actions.ts index 9b3316e..a79e5a2 100644 --- a/src/app/(auth)/actions.ts +++ b/src/app/(auth)/actions.ts @@ -7,19 +7,35 @@ export async function logout() { const cookieStore = cookies(); const accessToken = cookieStore.get("access_token")?.value; + // If no token, redirect to login if (!accessToken) { return redirect("/login"); } + // Clear cookies cookieStore.set("access_token", "", { path: "/", maxAge: 0, }); - cookieStore.set("user", "", { path: "/", maxAge: 0 }); + cookieStore.set("user", "", { + path: "/", + maxAge: 0, + }); cookieStore.set("refresh_token", "", { path: "/api/auth/refresh", maxAge: 0, }); - const keycloakLogoutUrl = `${process.env.NEXT_PUBLIC_KEYCLOAK_LOGOUT_URL || "http://localhost:8084/realms/kong/protocol/openid-connect/logout"}?client_id=kong-oidc&post_logout_redirect_uri=${encodeURIComponent(process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI || "http://localhost:3000/login")}`; - return redirect(keycloakLogoutUrl); + + // Prepare Keycloak logout URL + const logoutUrl = + process.env.NEXT_PUBLIC_KEYCLOAK_LOGOUT_URL || + "http://localhost:8084/realms/kong/protocol/openid-connect/logout"; + const redirectUri = + process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI || + "http://localhost:3000/login"; + + const fullLogoutUrl = `${logoutUrl}?client_id=kong-oidc&post_logout_redirect_uri=${encodeURIComponent(redirectUri)}`; + + // Perform redirect + return redirect(fullLogoutUrl); } From f5d36af98a206f6b359430d7bd39981c4ab2b761 Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Sun, 15 Jun 2025 17:42:40 +0530 Subject: [PATCH 03/10] update --- src/app/(auth)/actions.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/(auth)/actions.ts b/src/app/(auth)/actions.ts index a79e5a2..89c4be7 100644 --- a/src/app/(auth)/actions.ts +++ b/src/app/(auth)/actions.ts @@ -16,14 +16,20 @@ export async function logout() { cookieStore.set("access_token", "", { path: "/", maxAge: 0, + domain: ".go-together-uom.vercel.app", + secure: true, }); cookieStore.set("user", "", { path: "/", maxAge: 0, + domain: ".go-together-uom.vercel.app", + secure: true, }); cookieStore.set("refresh_token", "", { path: "/api/auth/refresh", maxAge: 0, + domain: ".go-together-uom.vercel.app", + secure: true, }); // Prepare Keycloak logout URL From 93d535418f43fb27a3e617d1761469425a6f6859 Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Sun, 15 Jun 2025 18:01:33 +0530 Subject: [PATCH 04/10] updates --- src/app/(auth)/actions.ts | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/app/(auth)/actions.ts b/src/app/(auth)/actions.ts index 89c4be7..fb8a48e 100644 --- a/src/app/(auth)/actions.ts +++ b/src/app/(auth)/actions.ts @@ -7,41 +7,29 @@ export async function logout() { const cookieStore = cookies(); const accessToken = cookieStore.get("access_token")?.value; - // If no token, redirect to login if (!accessToken) { return redirect("/login"); } - // Clear cookies - cookieStore.set("access_token", "", { + // Properly clear cookies + const cookieOptions = { path: "/", maxAge: 0, - domain: ".go-together-uom.vercel.app", secure: true, - }); - cookieStore.set("user", "", { - path: "/", - maxAge: 0, - domain: ".go-together-uom.vercel.app", - secure: true, - }); + domain: ".go-together-uom.vercel.app", // or remove this if same-site cookies + }; + + cookieStore.set("access_token", "", cookieOptions); cookieStore.set("refresh_token", "", { + ...cookieOptions, path: "/api/auth/refresh", - maxAge: 0, - domain: ".go-together-uom.vercel.app", - secure: true, }); + cookieStore.set("user", "", cookieOptions); - // Prepare Keycloak logout URL - const logoutUrl = - process.env.NEXT_PUBLIC_KEYCLOAK_LOGOUT_URL || - "http://localhost:8084/realms/kong/protocol/openid-connect/logout"; - const redirectUri = - process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI || - "http://localhost:3000/login"; + const logoutUrl = process.env.NEXT_PUBLIC_KEYCLOAK_LOGOUT_URL!; + const redirectUri = process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI!; const fullLogoutUrl = `${logoutUrl}?client_id=kong-oidc&post_logout_redirect_uri=${encodeURIComponent(redirectUri)}`; - // Perform redirect return redirect(fullLogoutUrl); } From f6bef228f0e07dd40c5eac3c614994e1953c4a2d Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Sun, 15 Jun 2025 20:56:59 +0530 Subject: [PATCH 05/10] update --- src/app/(auth)/login/GoogleSignInButton.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/(auth)/login/GoogleSignInButton.tsx b/src/app/(auth)/login/GoogleSignInButton.tsx index 917bf53..5181f82 100644 --- a/src/app/(auth)/login/GoogleSignInButton.tsx +++ b/src/app/(auth)/login/GoogleSignInButton.tsx @@ -8,10 +8,7 @@ export default function GoogleSignInButton() { asChild > From 8e70edcabae37c605aea94ed29f2a44185577254 Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Sun, 15 Jun 2025 21:52:51 +0530 Subject: [PATCH 06/10] update --- src/app/(auth)/login/GoogleSignInButton.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/(auth)/login/GoogleSignInButton.tsx b/src/app/(auth)/login/GoogleSignInButton.tsx index 5181f82..00339c5 100644 --- a/src/app/(auth)/login/GoogleSignInButton.tsx +++ b/src/app/(auth)/login/GoogleSignInButton.tsx @@ -8,7 +8,10 @@ export default function GoogleSignInButton() { asChild > From 2f7bc94eb5bbcfb8d8c62b854eec480fbbda73f8 Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Sun, 15 Jun 2025 22:46:16 +0530 Subject: [PATCH 07/10] update --- package-lock.json | 4 +- src/app/(main)/page.tsx | 396 ++++++++++++++---- src/components/home/horizontal-scroll-bar.tsx | 146 ++----- src/components/shared/ClientImage.tsx | 14 +- 4 files changed, 364 insertions(+), 196 deletions(-) diff --git a/package-lock.json b/package-lock.json index ddbc4dc..c46fd7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "nextjs-15-social-media-app", + "name": "GoTogether", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "nextjs-15-social-media-app", + "name": "GoTogether", "version": "0.1.0", "dependencies": { "@hookform/resolvers": "^3.9.0", diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx index dc8f88a..191ddec 100644 --- a/src/app/(main)/page.tsx +++ b/src/app/(main)/page.tsx @@ -15,7 +15,11 @@ import { ListChecks, Loader2, // Import Loader2 } from "lucide-react"; -import { getNearbyPlacesAction, geocodeAddressAction, GeocodeResult } from "./actions"; // Import geocodeAddressAction and GeocodeResult +import { + getNearbyPlacesAction, + geocodeAddressAction, + GeocodeResult, +} from "./actions"; // Import geocodeAddressAction and GeocodeResult import type { LocationDetail } from "@/types/location-types"; import { useTranslations } from "next-intl"; import { ROUTES, RouteKey } from "@/lib/routes"; // Import ROUTES and RouteKey @@ -24,17 +28,94 @@ import HorizontalScrollBar from "@/components/home/horizontal-scroll-bar"; // Fallback image definitions, updated to include vicinity and geometry const topPicksFallbackImages = [ - { id: "tp1", name: "Beach Paradise", image: "/assets/images/top-picks/img1.jpg", description: "Sun, sand, and sea.", geometry: { location: { lat: 0, lng: 0 } }, vicinity: "Popular Beach Area", category: "Nature", route: "TOP_PICKS" as RouteKey, place_id: "tp1_fallback", photo_urls: ["/assets/images/top-picks/img1.jpg"]}, - { id: "tp2", name: "Mountain Hike", image: "/assets/images/top-picks/img2.jpg", description: "Breathtaking views await.", geometry: { location: { lat: 0, lng: 0 } }, vicinity: "Scenic Mountain Trails", category: "Adventure", route: "TOP_PICKS" as RouteKey, place_id: "tp2_fallback", photo_urls: ["/assets/images/top-picks/img2.jpg"]}, - { id: "tp3", name: "City Exploration", image: "/assets/images/top-picks/img3.jpg", description: "Discover urban wonders.", geometry: { location: { lat: 0, lng: 0 } }, vicinity: "Downtown City Center", category: "Urban", route: "TOP_PICKS" as RouteKey, place_id: "tp3_fallback", photo_urls: ["/assets/images/top-picks/img3.jpg"]}, + { + id: "tp1", + name: "Beach Paradise", + image: "/assets/images/top-picks/img1.jpg", + description: "Sun, sand, and sea.", + geometry: { location: { lat: 0, lng: 0 } }, + vicinity: "Popular Beach Area", + category: "Nature", + route: "TOP_PICKS" as RouteKey, + place_id: "tp1_fallback", + photo_urls: ["/assets/images/top-picks/img1.jpg"], + }, + { + id: "tp2", + name: "Mountain Hike", + image: "/assets/images/top-picks/img2.jpg", + description: "Breathtaking views await.", + geometry: { location: { lat: 0, lng: 0 } }, + vicinity: "Scenic Mountain Trails", + category: "Adventure", + route: "TOP_PICKS" as RouteKey, + place_id: "tp2_fallback", + photo_urls: ["/assets/images/top-picks/img2.jpg"], + }, + { + id: "tp3", + name: "City Exploration", + image: "/assets/images/top-picks/img3.jpg", + description: "Discover urban wonders.", + geometry: { location: { lat: 0, lng: 0 } }, + vicinity: "Downtown City Center", + category: "Urban", + route: "TOP_PICKS" as RouteKey, + place_id: "tp3_fallback", + photo_urls: ["/assets/images/top-picks/img3.jpg"], + }, ]; const entertainmentFallbackImages = [ - { id: "e1", name: "Live Music Venue", image: "/assets/images/entertainment/img1.jpg", description: "Local bands and artists.", geometry: { location: { lat: 0, lng: 0 } }, vicinity: "Entertainment District", category: "Music", route: "ENTERTAINMENT" as RouteKey, place_id: "e1_fallback", photo_urls: ["/assets/images/entertainment/img1.jpg"]}, - { id: "e2", name: "Cinema Complex", image: "/assets/images/entertainment/img2.jpg", description: "Latest movie releases.", geometry: { location: { lat: 0, lng: 0 } }, vicinity: "Mall Area", category: "Movies", route: "ENTERTAINMENT" as RouteKey, place_id: "e2_fallback", photo_urls: ["/assets/images/entertainment/img2.jpg"]}, + { + id: "e1", + name: "Live Music Venue", + image: "/assets/images/entertainment/img1.jpg", + description: "Local bands and artists.", + geometry: { location: { lat: 0, lng: 0 } }, + vicinity: "Entertainment District", + category: "Music", + route: "ENTERTAINMENT" as RouteKey, + place_id: "e1_fallback", + photo_urls: ["/assets/images/entertainment/img1.jpg"], + }, + { + id: "e2", + name: "Cinema Complex", + image: "/assets/images/entertainment/img2.jpg", + description: "Latest movie releases.", + geometry: { location: { lat: 0, lng: 0 } }, + vicinity: "Mall Area", + category: "Movies", + route: "ENTERTAINMENT" as RouteKey, + place_id: "e2_fallback", + photo_urls: ["/assets/images/entertainment/img2.jpg"], + }, ]; const cultureFallbackImages = [ - { id: "c1", name: "Historical Museum", image: "/assets/images/culture/img1.jpg", description: "Artifacts and exhibits.", geometry: { location: { lat: 0, lng: 0 } }, vicinity: "Old Town", category: "History", route: "CULTURE" as RouteKey, place_id: "c1_fallback", photo_urls: ["/assets/images/culture/img1.jpg"]}, - { id: "c2", name: "Art Installation", image: "/assets/images/culture/img2.jpg", description: "Contemporary art pieces.", geometry: { location: { lat: 0, lng: 0 } }, vicinity: "Art District", category: "Art", route: "CULTURE" as RouteKey, place_id: "c2_fallback", photo_urls: ["/assets/images/culture/img2.jpg"]}, + { + id: "c1", + name: "Historical Museum", + image: "/assets/images/culture/img1.jpg", + description: "Artifacts and exhibits.", + geometry: { location: { lat: 0, lng: 0 } }, + vicinity: "Old Town", + category: "History", + route: "CULTURE" as RouteKey, + place_id: "c1_fallback", + photo_urls: ["/assets/images/culture/img1.jpg"], + }, + { + id: "c2", + name: "Art Installation", + image: "/assets/images/culture/img2.jpg", + description: "Contemporary art pieces.", + geometry: { location: { lat: 0, lng: 0 } }, + vicinity: "Art District", + category: "Art", + route: "CULTURE" as RouteKey, + place_id: "c2_fallback", + photo_urls: ["/assets/images/culture/img2.jpg"], + }, ]; interface SectionDataState { @@ -60,59 +141,114 @@ export default function HomeScreen() { const [isGeocoding, setIsGeocoding] = useState(false); const homeSectionsConfig = [ - { id: "topPicks", titleKey: "topPicksTitle", queryTypes: ["tourist_attraction", "park"], radius: 5000, fallbackImageSet: topPicksFallbackImages, scrollButtonKey: "TOP_PICKS" as RouteKey }, - { id: "entertainment", titleKey: "entertainmentTitle", queryTypes: ["movie_theater", "night_club"], radius: 5000, fallbackImageSet: entertainmentFallbackImages, scrollButtonKey: "ENTERTAINMENT" as RouteKey }, - { id: "culture", titleKey: "cultureTitle", queryTypes: ["museum", "art_gallery"], radius: 5000, fallbackImageSet: cultureFallbackImages, scrollButtonKey: "CULTURE" as RouteKey }, + { + id: "topPicks", + titleKey: "topPicksTitle", + queryTypes: ["tourist_attraction", "park"], + radius: 5000, + fallbackImageSet: topPicksFallbackImages, + scrollButtonKey: "TOP_PICKS" as RouteKey, + }, + { + id: "entertainment", + titleKey: "entertainmentTitle", + queryTypes: ["movie_theater", "night_club"], + radius: 5000, + fallbackImageSet: entertainmentFallbackImages, + scrollButtonKey: "ENTERTAINMENT" as RouteKey, + }, + { + id: "culture", + titleKey: "cultureTitle", + queryTypes: ["museum", "art_gallery"], + radius: 5000, + fallbackImageSet: cultureFallbackImages, + scrollButtonKey: "CULTURE" as RouteKey, + }, ]; const getCurrentLatLng = (): Promise<{ lat: number; lng: number }> => { return new Promise((resolve) => { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( - (position) => resolve({ lat: position.coords.latitude, lng: position.coords.longitude }), + (position) => + resolve({ + lat: position.coords.latitude, + lng: position.coords.longitude, + }), (error) => { console.error("Error getting location:", error); - setErrorMsg("Permission to access location was denied. Using default location."); - resolve({ lat: 40.7128, lng: -74.006 }); // Default: New York + setErrorMsg( + "Location permission denied. Showing nearby places based on default location.", + ); + resolve({ lat: 40.7128, lng: -74.006 }); // New York fallback }, ); } else { - setErrorMsg("Geolocation is not supported. Using default location."); - resolve({ lat: 40.7128, lng: -74.006 }); // Default: New York + setErrorMsg("Geolocation not supported. Using default location."); + resolve({ lat: 40.7128, lng: -74.006 }); } }); }; useEffect(() => { - if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition( - (position) => setLocationDisplay(`Lat: ${position.coords.latitude.toFixed(2)}, Lon: ${position.coords.longitude.toFixed(2)}`), - (error) => { setErrorMsg("Permission to access location was denied."); console.error(error); }, - ); - } else { - setErrorMsg("Geolocation is not supported by this browser."); - } - - const fetchAllSectionsData = async () => { + const fetchLocationAndData = async () => { const { lat, lng } = await getCurrentLatLng(); + setLocationDisplay(`Lat: ${lat.toFixed(2)}, Lon: ${lng.toFixed(2)}`); + const locationString = `${lat},${lng}`; + homeSectionsConfig.forEach((section) => { startTransition(async () => { - setSectionsData((prevData) => ({ ...prevData, [section.id]: { data: [], isLoading: true, error: null } })); + setSectionsData((prevData) => ({ + ...prevData, + [section.id]: { data: [], isLoading: true, error: null }, + })); + try { - const result = await getNearbyPlacesAction(locationString, section.queryTypes, section.radius); + const result = await getNearbyPlacesAction( + locationString, + section.queryTypes, + section.radius, + ); + if (result.success && result.data) { - setSectionsData((prevData) => ({ ...prevData, [section.id]: { data: result.data!.slice(0, 10), isLoading: false, error: null } })); + setSectionsData((prevData) => ({ + ...prevData, + [section.id]: { + data: result.data!.slice(0, 10), + isLoading: false, + error: null, + }, + })); } else { - setSectionsData((prevData) => ({ ...prevData, [section.id]: { data: [], isLoading: false, error: result.error || `Failed to fetch ${t(section.titleKey)}.` } })); + setSectionsData((prevData) => ({ + ...prevData, + [section.id]: { + data: [], + isLoading: false, + error: + result.error || `Failed to fetch ${t(section.titleKey)}.`, + }, + })); } } catch (error) { - setSectionsData((prevData) => ({ ...prevData, [section.id]: { data: [], isLoading: false, error: (error as Error).message || `Exception fetching ${t(section.titleKey)}.` } })); + setSectionsData((prevData) => ({ + ...prevData, + [section.id]: { + data: [], + isLoading: false, + error: + (error as Error).message || + `Exception fetching ${t(section.titleKey)}.`, + }, + })); } }); }); }; - fetchAllSectionsData(); + + fetchLocationAndData(); }, [t]); const handleSearchSubmit = async (query: string) => { @@ -148,21 +284,63 @@ export default function HomeScreen() { const handleNavigation = (routeKey: RouteKey) => { const path = ROUTES[routeKey]; - if (path) router.push(path); else console.error("Error: Invalid routeKey or path not found:", routeKey); + if (path) router.push(path); + else console.error("Error: Invalid routeKey or path not found:", routeKey); }; - const handleExternalLink = (url: string) => window.open(url, "_blank", "noopener noreferrer"); + const handleExternalLink = (url: string) => + window.open(url, "_blank", "noopener noreferrer"); const iconSize = 28; const cardButtons = [ - { title: "Stay", icon: , color: "text-red-500", onPress: () => handleNavigation("STAY") }, - { title: "Food", icon: , color: "text-orange-500", onPress: () => handleNavigation("FOOD") }, - { title: "Culture", icon: , color: "text-yellow-500", onPress: () => handleNavigation("CULTURE") }, - { title: "Entertainment", icon: , color: "text-green-500", onPress: () => handleNavigation("ENTERTAINMENT") }, - { title: "Nearby", icon: , color: "text-teal-500", onPress: () => handleNavigation("NEARBY") }, - { title: "Top Picks", icon: , color: "text-blue-500", onPress: () => handleNavigation("TOP_PICKS") }, - { title: "Emergency", icon: , color: "text-purple-500", onPress: () => handleNavigation("EMERGENCY") }, - { title: "Planner", icon: , color: "text-pink-500", onPress: () => handleNavigation("PLANNER") }, + { + title: "Stay", + icon: , + color: "text-red-500", + onPress: () => handleNavigation("STAY"), + }, + { + title: "Food", + icon: , + color: "text-orange-500", + onPress: () => handleNavigation("FOOD"), + }, + { + title: "Culture", + icon: , + color: "text-yellow-500", + onPress: () => handleNavigation("CULTURE"), + }, + { + title: "Entertainment", + icon: , + color: "text-green-500", + onPress: () => handleNavigation("ENTERTAINMENT"), + }, + { + title: "Nearby", + icon: , + color: "text-teal-500", + onPress: () => handleNavigation("NEARBY"), + }, + { + title: "Top Picks", + icon: , + color: "text-blue-500", + onPress: () => handleNavigation("TOP_PICKS"), + }, + { + title: "Emergency", + icon: , + color: "text-purple-500", + onPress: () => handleNavigation("EMERGENCY"), + }, + { + title: "Planner", + icon: , + color: "text-pink-500", + onPress: () => handleNavigation("PLANNER"), + }, ]; return ( @@ -171,12 +349,18 @@ export default function HomeScreen() {
-

{t("greeting")}

+

+ {t("greeting")} +

- {locationDisplay ? `Current Location: ${locationDisplay}` : errorMsg || "Fetching location..."} + {locationDisplay + ? `Current Location: ${locationDisplay}` + : errorMsg || "Fetching location..."}

-
U
+
+ U +
setSearchQuery(e.target.value)} onKeyDown={(e) => { - if (e.key === 'Enter') { + if (e.key === "Enter") { e.preventDefault(); // Prevent default if part of a form handleSearchSubmit(searchQuery); } @@ -199,34 +383,53 @@ export default function HomeScreen() {
{/* Geocoding Results Section */} -
{/* Added padding to match main content area */} +
+ {" "} + {/* Added padding to match main content area */} {isGeocoding && ( -
+
-

Geocoding address...

+

+ Geocoding address... +

)} {geocodingError && ( -
+
{geocodingError}
)} {!isGeocoding && !geocodingError && geocodedResults.length > 0 && ( -
-

Geocoding Results:

{/* Updated classes */} +
+

+ Geocoding Results: +

{" "} + {/* Updated classes */}
    {geocodedResults.map((result) => ( -
  • -

    {result.formatted_address}

    -

    Coordinates: Lat: {result.geometry.location.lat.toFixed(5)}, Lng: {result.geometry.location.lng.toFixed(5)}

    +
  • +

    + {result.formatted_address} +

    +

    + Coordinates: Lat:{" "} + {result.geometry.location.lat.toFixed(5)}, Lng:{" "} + {result.geometry.location.lng.toFixed(5)} +

  • ))}
)} - {!isGeocoding && !geocodingError && geocodedResults.length === 0 && searchQuery && ( -
- {/* This message shows after a search yields no results and isn't an error, or if search was cleared. + {!isGeocoding && + !geocodingError && + geocodedResults.length === 0 && + searchQuery && ( +
+ {/* This message shows after a search yields no results and isn't an error, or if search was cleared. To be more specific, a "No results found" is handled by geocodingError state. This part might need refinement based on desired UX for "empty search after submit" vs "initial state". For now, let's assume geocodingError handles "No results found." @@ -234,12 +437,16 @@ export default function HomeScreen() { So, this specific condition might not be hit if "No results" is always an error message. Let's remove this or make it more specific to "type to search". */} -
- )} +
+ )}
-
{/* Adjusted padding top to 0 as results section above has padding */} -
{/* Added mt-6 */} +
+ {" "} + {/* Adjusted padding top to 0 as results section above has padding */} +
+ {" "} + {/* Added mt-6 */}
{cardButtons.map((button) => ( ))}
- {homeSectionsConfig.map((sectionConfig) => { - const sectionState = sectionsData[sectionConfig.id] || { data: [], isLoading: true, error: null }; - const cardDataForScroll = sectionState.isLoading || sectionState.error || sectionState.data.length === 0 - ? sectionConfig.fallbackImageSet - : sectionState.data; + const sectionState = sectionsData[sectionConfig.id] || { + data: [], + isLoading: true, + error: null, + }; + const cardDataForScroll = + sectionState.isLoading || + sectionState.error || + sectionState.data.length === 0 + ? sectionConfig.fallbackImageSet + : sectionState.data; const imagesForScroll = cardDataForScroll.map((place) => { - if (place.photo_urls && place.photo_urls.length > 0) return place.photo_urls[0]; + if (place.photo_urls && place.photo_urls.length > 0) + return place.photo_urls[0]; if ((place as any).image) return (place as any).image; return "/assets/images/default-placeholder.png"; }); const finalCardData = cardDataForScroll.map((item, idx) => ({ - ...item, - place_id: item.place_id || (item as any).id || `fallback-${sectionConfig.id}-${idx}`, - name: item.name || (item as any).title || "Unknown Place", + ...item, + place_id: + item.place_id || + (item as any).id || + `fallback-${sectionConfig.id}-${idx}`, + name: item.name || (item as any).title || "Unknown Place", })); return ( -
{/* Added wrapper div with mb-6 */} +
+ {" "} + {/* Added wrapper div with mb-6 */}
); })} - -
{/* my-6 is already here, good */} -

Plan Your Stay

+
+ {" "} + {/* my-6 is already here, good */} +

+ Plan Your Stay +

-

© {new Date().getFullYear()} GoTogether. All rights reserved.

+

+ © {new Date().getFullYear()} GoTogether. All rights reserved. +

diff --git a/src/components/home/horizontal-scroll-bar.tsx b/src/components/home/horizontal-scroll-bar.tsx index 67d5310..af92263 100644 --- a/src/components/home/horizontal-scroll-bar.tsx +++ b/src/components/home/horizontal-scroll-bar.tsx @@ -1,13 +1,13 @@ "use client"; import { LocationDetail } from "@/types/location-types"; -import React, { useRef, useState, useEffect, useCallback } from "react"; -import { ChevronLeft, ChevronRight } from "lucide-react"; -import PlaceCard from "./PlaceCard"; // Import the new PlaceCard component -import type { RouteKey } from "@/lib/routes"; // Import RouteKey +import React from "react"; +import PlaceCard from "./PlaceCard"; +import type { RouteKey } from "@/lib/routes"; +import { ChevronRight } from "lucide-react"; interface ScrollButton { - route: RouteKey; // Changed string to RouteKey + route: RouteKey; loading: boolean; } @@ -15,124 +15,56 @@ interface HorizontalScrollBarProps { title: string; cardData: LocationDetail[]; scrollButton: ScrollButton; - handleNavigation: (route: RouteKey) => void; // Changed string to RouteKey + handleNavigation: (route: RouteKey) => void; images: string[]; } -const HorizontalScrollBar = ( - props: HorizontalScrollBarProps, -): React.JSX.Element => { - const { title, cardData, scrollButton, handleNavigation, images } = props; - const scrollContainerRef = useRef(null); - const [canScrollLeft, setCanScrollLeft] = useState(false); - const [canScrollRight, setCanScrollRight] = useState(false); - - const updateScrollButtonState = useCallback(() => { - if (scrollContainerRef.current) { - const { scrollLeft, scrollWidth, clientWidth } = - scrollContainerRef.current; - setCanScrollLeft(scrollLeft > 5); - setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 5); - } - }, []); - - useEffect(() => { - const scrollElement = scrollContainerRef.current; - if (!scrollElement) return; - - updateScrollButtonState(); - - scrollElement.addEventListener("scroll", updateScrollButtonState); - const resizeObserver = new ResizeObserver(updateScrollButtonState); - resizeObserver.observe(scrollElement); - const mutationObserver = new MutationObserver(updateScrollButtonState); - mutationObserver.observe(scrollElement, { childList: true, subtree: true }); - - return () => { - scrollElement.removeEventListener("scroll", updateScrollButtonState); - resizeObserver.disconnect(); - mutationObserver.disconnect(); - }; - }, [cardData, updateScrollButtonState]); - - const handleScrollLeft = () => { - if (scrollContainerRef.current) { - scrollContainerRef.current.scrollBy({ left: -300, behavior: "smooth" }); - } - }; - - const handleScrollRight = () => { - if (scrollContainerRef.current) { - scrollContainerRef.current.scrollBy({ left: 300, behavior: "smooth" }); - } - }; +const HorizontalScrollBar = ({ + title, + cardData, + scrollButton, + handleNavigation, + images, +}: HorizontalScrollBarProps): React.JSX.Element => { + const maxCards = 3; + const displayedCards = cardData.slice(0, maxCards); return ( -
-
-

{/* Changed font-bold to font-semibold */} +
+
+

{title}

-
- + {scrollButton?.route && ( - {scrollButton && scrollButton.route && scrollButton.route.trim() !== "" && ( - - )} -
+ )}
{scrollButton.loading ? ( -

+

Loading...

+ ) : displayedCards.length === 0 ? ( +

+ No items to display currently. +

) : ( -
- {cardData.length === 0 && !scrollButton.loading && ( -

- No items to display currently. -

- )} - {cardData.map((item, index) => { - const imagePath = - images?.[index] ?? "/assets/images/default-placeholder.png"; - - return ( - - ); - })} +
+ {displayedCards.map((item, index) => ( + + ))}
)}
diff --git a/src/components/shared/ClientImage.tsx b/src/components/shared/ClientImage.tsx index b735860..1c64c6f 100644 --- a/src/components/shared/ClientImage.tsx +++ b/src/components/shared/ClientImage.tsx @@ -12,7 +12,7 @@ interface ClientImageProps { priority?: boolean; className?: string; objectFit?: "cover" | "contain" | "fill"; - sizes?: string; // Added sizes prop + sizes?: string; } export default function ClientImage({ @@ -24,13 +24,17 @@ export default function ClientImage({ priority = false, className, objectFit = "cover", - sizes, // Added sizes to destructuring + sizes, }: ClientImageProps) { const [imgSrc, setImgSrc] = useState(src); + const [hasError, setHasError] = useState(false); const handleError = () => { - console.error(`Failed to load image at ${src}`); - setImgSrc("/assets/images/default-placeholder.png"); + if (!hasError) { + console.warn(`Image failed to load: ${imgSrc}`); + setImgSrc("/assets/images/default-placeholder.png"); + setHasError(true); + } }; return ( @@ -44,7 +48,7 @@ export default function ClientImage({ onError={handleError} className={className} style={{ objectFit }} - sizes={sizes} // Passed sizes to Image component + sizes={sizes} /> ); } From 554302cba200aae7176b8fed206f59513db8f63e Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Mon, 16 Jun 2025 10:37:36 +0530 Subject: [PATCH 08/10] update --- src/app/(auth)/actions.ts | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/src/app/(auth)/actions.ts b/src/app/(auth)/actions.ts index fb8a48e..e618690 100644 --- a/src/app/(auth)/actions.ts +++ b/src/app/(auth)/actions.ts @@ -1,35 +1,14 @@ -"use server"; - -import { cookies } from "next/headers"; -import { redirect } from "next/navigation"; - -export async function logout() { - const cookieStore = cookies(); - const accessToken = cookieStore.get("access_token")?.value; - - if (!accessToken) { - return redirect("/login"); - } - - // Properly clear cookies - const cookieOptions = { - path: "/", - maxAge: 0, - secure: true, - domain: ".go-together-uom.vercel.app", // or remove this if same-site cookies - }; - - cookieStore.set("access_token", "", cookieOptions); - cookieStore.set("refresh_token", "", { - ...cookieOptions, - path: "/api/auth/refresh", - }); - cookieStore.set("user", "", cookieOptions); - +export function logout() { + // Clear client-side cookies + document.cookie = "access_token=; path=/; max-age=0; Secure; SameSite=Lax"; + document.cookie = "user=; path=/; max-age=0; Secure; SameSite=Lax"; + document.cookie = + "refresh_token=; path=/api/auth/refresh; max-age=0; Secure; SameSite=Lax"; + + // Redirect to Keycloak logout endpoint const logoutUrl = process.env.NEXT_PUBLIC_KEYCLOAK_LOGOUT_URL!; const redirectUri = process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI!; - const fullLogoutUrl = `${logoutUrl}?client_id=kong-oidc&post_logout_redirect_uri=${encodeURIComponent(redirectUri)}`; - return redirect(fullLogoutUrl); + window.location.href = fullLogoutUrl; } From f1a9a103ce1dba4a08fe8a56ac0bc4f501d7a5ad Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Mon, 16 Jun 2025 10:45:42 +0530 Subject: [PATCH 09/10] update --- src/app/(auth)/actions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/(auth)/actions.ts b/src/app/(auth)/actions.ts index e618690..a9b6e09 100644 --- a/src/app/(auth)/actions.ts +++ b/src/app/(auth)/actions.ts @@ -7,8 +7,8 @@ export function logout() { // Redirect to Keycloak logout endpoint const logoutUrl = process.env.NEXT_PUBLIC_KEYCLOAK_LOGOUT_URL!; - const redirectUri = process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI!; - const fullLogoutUrl = `${logoutUrl}?client_id=kong-oidc&post_logout_redirect_uri=${encodeURIComponent(redirectUri)}`; + const redirectUri = process.env.NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI!.trim(); + const fullLogoutUrl = `${logoutUrl}?client_id=kong-oidc&post_logout_redirect_uri=${encodeURIComponent(redirectUri)}`; window.location.href = fullLogoutUrl; } From 8b698ea1f589176a344249c3967a0c5cb94eb80b Mon Sep 17 00:00:00 2001 From: Harshana-2000 Date: Mon, 16 Jun 2025 11:01:21 +0530 Subject: [PATCH 10/10] fixed logout in production --- src/app/(auth)/login/actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(auth)/login/actions.ts b/src/app/(auth)/login/actions.ts index 969051c..666aad9 100644 --- a/src/app/(auth)/login/actions.ts +++ b/src/app/(auth)/login/actions.ts @@ -46,7 +46,7 @@ export async function login( cookieStore.set("access_token", accessToken, { path: "/", - httpOnly: true, + httpOnly: false, secure: process.env.NODE_ENV === "production", maxAge: 3600, // Or use expiresIn from backend response if available and preferred });