show upcomingCities from backend on landingPage

This commit is contained in:
aryabenade
2026-03-20 13:58:39 +05:30
parent 0a60ba58a3
commit 5d213d14d8
6 changed files with 258 additions and 213 deletions

17
src/Redux/baseQuery.ts Normal file
View File

@@ -0,0 +1,17 @@
// src/store/baseQuery.ts
import { fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const baseQuery = fetchBaseQuery({
baseUrl: import.meta.env.VITE_BASE_URL,
// credentials: "include",
prepareHeaders: (headers) => {
const token = localStorage.getItem("accessToken");
if (token) {
headers.set("Authorization", `Bearer ${token}`);
// headers.set("access-token", token);
}
// headers.set("Content-Type", "application/json");
return headers;
},
});

View File

@@ -1,10 +1,12 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { baseQuery } from "../baseQuery";
export const attractionsApi = createApi({
reducerPath: 'attractionsApi',
baseQuery: fetchBaseQuery({
baseUrl: 'https://testingapi.citycards.betadelivery.com',
}),
// baseQuery: fetchBaseQuery({
// baseUrl: 'https://testingapi.citycards.betadelivery.com',
// }),
baseQuery,
endpoints: (builder) => ({
getAttractionFilters: builder.query({
// cityId is passed as the query param

View File

@@ -1,10 +1,12 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { baseQuery } from "../baseQuery";
export const citiesApi = createApi({
reducerPath: 'citiesApi',
baseQuery: fetchBaseQuery({
baseUrl: 'https://testingapi.citycards.betadelivery.com',
}),
// baseQuery: fetchBaseQuery({
// baseUrl: 'https://testingapi.citycards.betadelivery.com',
// }),
baseQuery,
endpoints: (builder) => ({
getCityListWithBanner: builder.query({
@@ -15,8 +17,14 @@ export const citiesApi = createApi({
return `/cities/list/customer/cities?${params.toString()}`
}
}),
getUpcomingCities: builder.query({
query: (listType) => `/cities/list/all?listType=${listType}`,
})
}),
});
export const { useGetCityListWithBannerQuery} = citiesApi;
export const { useGetCityListWithBannerQuery,useGetUpcomingCitiesQuery } = citiesApi;

View File

@@ -3,102 +3,103 @@ import { ImageWithFallback } from './figma/ImageWithFallback';
import { Button } from './ui/button';
import { useRef, useState, useEffect } from 'react';
import Image592Traced from '../imports/Image592Traced-5025-559';
import { useGetUpcomingCitiesQuery } from '../Redux/services/cities.service';
const upcomingCities = [
{
id: 1,
name: 'Boston',
country: 'USA',
launchDate: 'Spring 2025',
attractions: 65,
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.',
image: 'https://images.unsplash.com/photo-1568271667303-14b2a1a36da1?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: true
},
{
id: 2,
name: 'Rome',
country: 'Italy',
launchDate: 'Summer 2025',
attractions: 80,
image: 'https://images.unsplash.com/photo-1552832230-c0197dd311b5?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false
},
{
id: 3,
name: 'Paris',
country: 'France',
launchDate: 'Fall 2025',
attractions: 95,
image: 'https://images.unsplash.com/photo-1502602898536-47ad22581b52?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false
},
{
id: 4,
name: 'Dubai',
country: 'UAE',
launchDate: 'Winter 2025',
attractions: 70,
image: 'https://images.unsplash.com/photo-1512453979798-5ea266f8880c?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false,
badge: 'New'
},
{
id: 5,
name: 'Tokyo',
country: 'Japan',
launchDate: 'Early 2026',
attractions: 120,
image: 'https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false
},
{
id: 6,
name: 'Sydney',
country: 'Australia',
launchDate: 'Spring 2026',
attractions: 85,
image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false
},
{
id: 7,
name: 'New York',
country: 'USA',
launchDate: 'Summer 2026',
attractions: 150,
image: 'https://images.unsplash.com/photo-1496442226666-8d4d0e62e6e9?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false,
badge: 'Most Requested'
},
{
id: 8,
name: 'Singapore',
country: 'Singapore',
launchDate: 'Fall 2026',
attractions: 75,
image: 'https://images.unsplash.com/photo-1525625293386-3f8f99389edd?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false
},
{
id: 9,
name: 'Amsterdam',
country: 'Netherlands',
launchDate: 'Winter 2026',
attractions: 90,
image: 'https://images.unsplash.com/photo-1534351590666-13e3e96b5017?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false
},
{
id: 10,
name: 'Barcelona',
country: 'Spain',
launchDate: 'Early 2027',
attractions: 110,
image: 'https://images.unsplash.com/photo-1583422409516-2895a77efded?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
showHoverState: false
}
];
// const upcomingCities = [
// {
// id: 1,
// name: 'Boston',
// country: 'USA',
// launchDate: 'Spring 2025',
// attractions: 65,
// description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.',
// image: 'https://images.unsplash.com/photo-1568271667303-14b2a1a36da1?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: true
// },
// {
// id: 2,
// name: 'Rome',
// country: 'Italy',
// launchDate: 'Summer 2025',
// attractions: 80,
// image: 'https://images.unsplash.com/photo-1552832230-c0197dd311b5?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false
// },
// {
// id: 3,
// name: 'Paris',
// country: 'France',
// launchDate: 'Fall 2025',
// attractions: 95,
// image: 'https://images.unsplash.com/photo-1502602898536-47ad22581b52?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false
// },
// {
// id: 4,
// name: 'Dubai',
// country: 'UAE',
// launchDate: 'Winter 2025',
// attractions: 70,
// image: 'https://images.unsplash.com/photo-1512453979798-5ea266f8880c?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false,
// badge: 'New'
// },
// {
// id: 5,
// name: 'Tokyo',
// country: 'Japan',
// launchDate: 'Early 2026',
// attractions: 120,
// image: 'https://images.unsplash.com/photo-1540959733332-eab4deabeeaf?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false
// },
// {
// id: 6,
// name: 'Sydney',
// country: 'Australia',
// launchDate: 'Spring 2026',
// attractions: 85,
// image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false
// },
// {
// id: 7,
// name: 'New York',
// country: 'USA',
// launchDate: 'Summer 2026',
// attractions: 150,
// image: 'https://images.unsplash.com/photo-1496442226666-8d4d0e62e6e9?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false,
// badge: 'Most Requested'
// },
// {
// id: 8,
// name: 'Singapore',
// country: 'Singapore',
// launchDate: 'Fall 2026',
// attractions: 75,
// image: 'https://images.unsplash.com/photo-1525625293386-3f8f99389edd?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false
// },
// {
// id: 9,
// name: 'Amsterdam',
// country: 'Netherlands',
// launchDate: 'Winter 2026',
// attractions: 90,
// image: 'https://images.unsplash.com/photo-1534351590666-13e3e96b5017?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false
// },
// {
// id: 10,
// name: 'Barcelona',
// country: 'Spain',
// launchDate: 'Early 2027',
// attractions: 110,
// image: 'https://images.unsplash.com/photo-1583422409516-2895a77efded?ixlib=rb-4.0.3&auto=format&fit=crop&w=600&q=80',
// showHoverState: false
// }
// ];
export function LandingUpcomingCities() {
const scrollContainerRef = useRef<HTMLDivElement>(null);
@@ -107,6 +108,15 @@ export function LandingUpcomingCities() {
const [scrollLeft, setScrollLeft] = useState(0);
const [showDragHint, setShowDragHint] = useState(false);
const listType = "upcomingCity"
// const[listType,setListType]=useState("upcomingCity")
const { data, isLoading } = useGetUpcomingCitiesQuery(listType)
if(isLoading){
return <div>Loading...</div>
}
const handleMouseDown = (e: React.MouseEvent) => {
if (!scrollContainerRef.current) return;
// Only start dragging if not clicking on a button or interactive element
@@ -143,11 +153,11 @@ export function LandingUpcomingCities() {
}
};
useEffect(() => {
const handleGlobalMouseUp = () => setIsDragging(false);
document.addEventListener('mouseup', handleGlobalMouseUp);
return () => document.removeEventListener('mouseup', handleGlobalMouseUp);
}, []);
// useEffect(() => {
// const handleGlobalMouseUp = () => setIsDragging(false);
// document.addEventListener('mouseup', handleGlobalMouseUp);
// return () => document.removeEventListener('mouseup', handleGlobalMouseUp);
// }, []);
return (
<section className="py-20 bg-gray-50">
@@ -172,11 +182,11 @@ export function LandingUpcomingCities() {
</div>
)}
<div
<div
ref={scrollContainerRef}
className={`flex gap-6 overflow-x-auto scrollbar-hide pb-2 ${isDragging ? 'cursor-grabbing dragging select-none' : 'cursor-grab'}`}
style={{
scrollbarWidth: 'none',
style={{
scrollbarWidth: 'none',
msOverflowStyle: 'none',
scrollBehavior: isDragging ? 'auto' : 'smooth',
paddingLeft: 'max(1rem, calc((100vw - 1280px) / 2 + 1rem))',
@@ -188,112 +198,112 @@ export function LandingUpcomingCities() {
onMouseMove={handleMouseMove}
onMouseEnter={handleMouseEnter}
>
{upcomingCities.map((city) => (
<div
key={city.id}
className="flex-shrink-0 w-72 md:w-80 group relative h-[420px] rounded-3xl overflow-hidden shadow-lg hover:shadow-xl transition-all duration-500"
>
{/* Background - Either solid color or image */}
{city.showHoverState ? (
// Boston card with image background and same layout as other cards
<>
<ImageWithFallback
src={city.image!}
alt={city.name}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700"
/>
{/* Dark overlay */}
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent group-hover:from-black/80 transition-all duration-500" />
{/* City name overlay - matching Rome card layout */}
<div className="absolute bottom-6 left-6 right-6 text-white">
<h3 className="text-2xl font-bold mb-2">{city.name}</h3>
<div className="flex items-center justify-between text-sm text-white/80">
<span>{city.country}</span>
<span>{city.launchDate}</span>
</div>
</div>
{data && data?.upcomingCities?.map((city: any) => (
<div
key={city.id}
className="flex-shrink-0 w-72 md:w-80 group relative h-[420px] rounded-3xl overflow-hidden shadow-lg hover:shadow-xl transition-all duration-500"
>
{/* Background - Either solid color or image */}
{true ? (
// Boston card with image background and same layout as other cards
<>
<ImageWithFallback
src={city.imgPathName!}
alt={city.cityName}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700"
/>
{/* Hover state overlay - same as other cards */}
<div className="absolute inset-0 bg-warm-coral/90 opacity-0 group-hover:opacity-100 transition-all duration-500 flex items-center justify-center">
<div className="text-center text-white">
<h3 className="text-2xl font-bold mb-2">{city.name}</h3>
<p className="text-white/90 mb-4">{city.attractions}+ attractions</p>
<p className="text-sm text-white/80 mb-6">Coming {city.launchDate}</p>
<Button
variant="secondary"
className="bg-white/20 hover:bg-white/30 text-white border-white/30 hover:border-white/50 backdrop-blur-sm"
onMouseDown={(e) => {
e.stopPropagation();
setIsDragging(false);
}}
onClick={(e) => {
e.stopPropagation();
console.log('Notify Me button clicked');
}}
>
Notify Me
<ArrowRight className="w-4 h-4 ml-2" />
</Button>
</div>
</div>
</>
) : (
// Image background for other cards
<>
<ImageWithFallback
src={city.image!}
alt={city.name}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700"
/>
{/* Dark overlay */}
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent group-hover:from-black/80 transition-all duration-500" />
{/* Badge (if present) */}
{city.badge && (
<div className="absolute top-4 right-4 bg-white text-gray-900 px-3 py-1 rounded-full text-sm font-medium shadow-lg">
{city.badge}
</div>
)}
{/* Dark overlay */}
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent group-hover:from-black/80 transition-all duration-500" />
{/* City name overlay */}
<div className="absolute bottom-6 left-6 right-6 text-white">
<h3 className="text-2xl font-bold mb-2">{city.name}</h3>
<div className="flex items-center justify-between text-sm text-white/80">
<span>{city.country}</span>
<span>{city.launchDate}</span>
</div>
{/* City name overlay - matching Rome card layout */}
<div className="absolute bottom-6 left-6 right-6 text-white">
<h3 className="text-2xl font-bold mb-2">{city.cityName}</h3>
<div className="flex items-center justify-between text-sm text-white/80">
{/* <span>{city.country}</span>
<span>{city.launchDate}</span> */}
</div>
</div>
{/* Hover state overlay */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/90 to-secondary/90 opacity-0 group-hover:opacity-100 transition-all duration-500 flex items-center justify-center">
<div className="text-center text-white">
<h3 className="text-2xl font-bold mb-2">{city.name}</h3>
<p className="text-white/90 mb-4">{city.attractions}+ attractions</p>
<p className="text-sm text-white/80 mb-6">Coming {city.launchDate}</p>
<Button
variant="secondary"
className="bg-white/20 hover:bg-white/30 text-white border-white/30 hover:border-white/50 backdrop-blur-sm"
onMouseDown={(e) => {
e.stopPropagation();
setIsDragging(false);
}}
onClick={(e) => {
e.stopPropagation();
console.log('Notify Me button clicked');
}}
>
Notify Me
<ArrowRight className="w-4 h-4 ml-2" />
</Button>
</div>
{/* Hover state overlay - same as other cards */}
<div className="absolute inset-0 bg-warm-coral/90 opacity-0 group-hover:opacity-100 transition-all duration-500 flex items-center justify-center">
<div className="text-center text-white">
<h3 className="text-2xl font-bold mb-2">{city.cityName}</h3>
{/* <p className="text-white/90 mb-4">{city.attractions}+ attractions</p>
<p className="text-sm text-white/80 mb-6">Coming {city.launchDate}</p> */}
<Button
variant="secondary"
className="bg-white/20 hover:bg-white/30 text-white border-white/30 hover:border-white/50 backdrop-blur-sm"
onMouseDown={(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setIsDragging(false);
}}
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
console.log('Notify Me button clicked');
}}
>
Notify Me
<ArrowRight className="w-4 h-4 ml-2" />
</Button>
</div>
</>
)}
</div>
))}
</div>
</>
) : (
// Image background for other cards
<>
<ImageWithFallback
src={city.image!}
alt={city.name}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700"
/>
{/* Dark overlay */}
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent group-hover:from-black/80 transition-all duration-500" />
{/* Badge (if present) */}
{/* {city.badge && (
<div className="absolute top-4 right-4 bg-white text-gray-900 px-3 py-1 rounded-full text-sm font-medium shadow-lg">
{city.badge}
</div>
)} */}
{/* City name overlay */}
{/* <div className="absolute bottom-6 left-6 right-6 text-white">
<h3 className="text-2xl font-bold mb-2">{city.name}</h3>
<div className="flex items-center justify-between text-sm text-white/80">
<span>{city.country}</span>
<span>{city.launchDate}</span>
</div>
</div> */}
{/* Hover state overlay */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/90 to-secondary/90 opacity-0 group-hover:opacity-100 transition-all duration-500 flex items-center justify-center">
<div className="text-center text-white">
<h3 className="text-2xl font-bold mb-2">{city.cityName}</h3>
{/* <p className="text-white/90 mb-4">{city.attractions}+ attractions</p> */}
{/* <p className="text-sm text-white/80 mb-6">Coming {city.launchDate}</p> */}
<Button
variant="secondary"
className="bg-white/20 hover:bg-white/30 text-white border-white/30 hover:border-white/50 backdrop-blur-sm"
onMouseDown={(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
setIsDragging(false);
}}
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
console.log('Notify Me button clicked');
}}
>
Notify Me
<ArrowRight className="w-4 h-4 ml-2" />
</Button>
</div>
</div>
</>
)}
</div>
))}
</div>
</div>

View File

@@ -91,7 +91,7 @@ export default function Navbar({
const { user, login, logout } = useAuth(); // from AuthContext
const protectedPaths = ["/passes", "/whats-included", "/","/melbourne"];
const protectedPaths = ["/passes", "/whats-included", "/", "/melbourne"];
const handleOpenLoginModal = () => {
if (!user && protectedPaths.includes(location.pathname)) {
@@ -289,7 +289,7 @@ export default function Navbar({
console.log('City selected from navbar:', cityId);
onCityChange(cityId);
if (cityId.toLowerCase() === 'melbourne') {
if (cityId.toLowerCase() === '1') {
setNavigationSource('melbourne');
navigate('/melbourne');
} else {

8
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
interface ImportMetaEnv {
readonly VITE_BASE_URL: string
readonly VITE_GOOGLE_MAP: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}