integrate the super savings offer api

This commit is contained in:
aryabenade
2026-04-16 17:24:56 +05:30
parent 8ad18cf7eb
commit a7098e81d6
4 changed files with 472 additions and 600 deletions

View File

@@ -88,7 +88,7 @@ export function AppRouter({
} />
{/* Home Route */}
<Route path="/:cityName/:cityId" element={
<Route path="/:cityName" element={
<motion.div key="home" {...pageTransition}>
<MelbournePage {...commonNavHandlers} />
</motion.div>
@@ -106,7 +106,7 @@ export function AppRouter({
} />
{/* Attractions Routes */}
<Route path="/:cityName/:cityId/attractions" element={
<Route path="/:cityName/attractions" element={
<motion.div key="attractions" {...pageTransition}>
<AttractionsPage {...commonNavHandlers} />
</motion.div>
@@ -265,7 +265,7 @@ export function AppRouter({
</motion.div>
} />
<Route path="/super-savings" element={
<Route path="/:cityName/super-savings" element={
<motion.div key="super-savings" {...pageTransition}>
<SuperSavingsPage {...commonNavHandlers} />
</motion.div>

View File

@@ -20,15 +20,28 @@ export const citiesApi = createApi({
}),
getUpcomingCities: builder.query({
query: (listType) => `/cities/list/all?listType=${listType}`,
}),
getSelectedCityDetails:builder.query({
getSelectedCityDetails: builder.query({
query: (cityId) => `/website/${cityId}`,
})
}),
getSelectedCityOffers: builder.query({
query: ({ cityId, categoryId, page, limit }) => {
const params = new URLSearchParams()
params.append('cityXid', cityId);
if (categoryId) params.append('categoryXid', categoryId);
if (page) params.append('page', page);
if (limit) params.append('limit', limit);
return `/website/super-savings/list/offers?${params.toString()}`;
}
}),
}),
});
export const { useGetCityListWithBannerQuery,useGetUpcomingCitiesQuery,useGetSelectedCityDetailsQuery } = citiesApi;
export const { useGetCityListWithBannerQuery, useGetUpcomingCitiesQuery, useGetSelectedCityDetailsQuery, useGetSelectedCityOffersQuery } = citiesApi;

View File

@@ -42,20 +42,9 @@ export function CitySelectionDialog({
const handleCityClick = (city: City) => {
console.log('Selected city:', city.cityName);
// ✅ Call the onCitySelect callback if provided (passing cityId)
if (onCitySelect) {
// onCitySelect(String(city.cityName));
// navigate(`/${city.cityName}/${city.id}`)
navigate(`/${slugify(city.cityName)}`);
localStorage.setItem("cityId", String(city.id))
localStorage.setItem("cityName", String(city.cityName))
} else {
// ✅ Default behavior: navigate to passes page
navigate(`/passes?city=${encodeURIComponent(city.cityName)}`);
}
onClose();
};

View File

@@ -15,6 +15,8 @@ import { MobileAppSection } from '../components/MobileAppSection';
import { ReviewsSection } from '../components/ReviewsSection';
import { TrustedCompanies } from '../components/TrustedCompanies';
import { Layout } from '../Layout';
import { useGetSelectedCityOffersQuery } from '../Redux/services/cities.service';
import LoadingSpinner from '../components/LoadingSpinner';
interface SuperSavingsPageProps {
onBackClick: () => void;
@@ -44,138 +46,6 @@ interface SuperSavingsPageProps {
user?: { email: string; name: string; } | null;
}
// Mock super savings data
const savingsData = [
{
id: '1',
business: 'Grand Hotels Melbourne',
title: 'Up to 50% Off on luxury hotel stays across Melbourne',
discount: '50% OFF',
savedAmount: 'Save up to $300',
image: 'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=400',
category: 'hotels'
},
{
id: '2',
business: 'Adventure Tours',
title: '40% Off on guided adventure tours and experiences',
discount: '40% OFF',
savedAmount: 'Save up to $150',
image: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=400',
category: 'tours'
},
{
id: '3',
business: 'Premium Spa & Wellness',
title: '45% Off on spa packages and wellness treatments',
discount: '45% OFF',
savedAmount: 'Save up to $200',
image: 'https://images.unsplash.com/photo-1544161515-4ab6ce6db874?w=400',
category: 'wellness'
},
{
id: '4',
business: 'Culinary Delights',
title: '35% Off on fine dining at Michelin-starred restaurants',
discount: '35% OFF',
savedAmount: 'Save up to $120',
image: 'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=400',
category: 'dining'
},
{
id: '5',
business: 'Entertainment Pass',
title: '60% Off on theater shows and concert tickets',
discount: '60% OFF',
savedAmount: 'Save up to $250',
image: 'https://images.unsplash.com/photo-1514306191717-452ec28c7814?w=400',
category: 'entertainment'
},
{
id: '6',
business: 'Museum Pass',
title: '55% Off on museum entries and special exhibitions',
discount: '55% OFF',
savedAmount: 'Save up to $180',
image: 'https://images.unsplash.com/photo-1566127992631-137a642a90f4?w=400',
category: 'museums'
},
{
id: '7',
business: 'Luxury Shopping',
title: '30% Off on designer boutiques and luxury shopping',
discount: '30% OFF',
savedAmount: 'Save up to $500',
image: 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=400',
category: 'shopping'
},
{
id: '8',
business: 'Water Sports',
title: '45% Off on water sports and beach activities',
discount: '45% OFF',
savedAmount: 'Save up to $175',
image: 'https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?w=400',
category: 'sports'
},
{
id: '9',
business: 'Wine Tasting Tours',
title: '40% Off on wine country tours and tastings',
discount: '40% OFF',
savedAmount: 'Save up to $160',
image: 'https://images.unsplash.com/photo-1506377247377-2a5b3b417ebb?w=400',
category: 'tours'
},
{
id: '10',
business: 'Family Fun Parks',
title: '50% Off on family entertainment and theme parks',
discount: '50% OFF',
savedAmount: 'Save up to $220',
image: 'https://images.unsplash.com/photo-1524850011238-e3d235c7d4c9?w=400',
category: 'entertainment'
},
{
id: '11',
business: 'Boutique Stays',
title: '55% Off on boutique hotels and bed & breakfasts',
discount: '55% OFF',
savedAmount: 'Save up to $280',
image: 'https://images.unsplash.com/photo-1551882547-ff40c63fe5fa?w=400',
category: 'hotels'
},
{
id: '12',
business: 'Art Galleries',
title: '35% Off on contemporary art galleries and workshops',
discount: '35% OFF',
savedAmount: 'Save up to $140',
image: 'https://images.unsplash.com/photo-1561214115-f2f134cc4912?w=400',
category: 'museums'
},
{
id: '13',
business: 'Luxury Cruises',
title: '65% Off on harbor cruises and yacht experiences',
discount: '65% OFF',
savedAmount: 'Save up to $400',
image: 'https://images.unsplash.com/photo-1544551763-46a013bb70d5?w=400',
category: 'tours'
}
];
const filterCategories = [
{ value: 'hotels', label: 'Hotels', count: 2 },
{ value: 'tours', label: 'Tours', count: 3 },
{ value: 'wellness', label: 'Wellness', count: 1 },
{ value: 'dining', label: 'Dining', count: 1 },
{ value: 'entertainment', label: 'Entertainment', count: 2 },
{ value: 'museums', label: 'Museums', count: 2 },
{ value: 'shopping', label: 'Shopping', count: 1 },
{ value: 'sports', label: 'Sports', count: 1 }
];
// Categories data for the Super Savings Categories section
const categoriesData = [
{
@@ -242,39 +112,28 @@ export function SuperSavingsPage({
currentPage,
user
}: SuperSavingsPageProps) {
const [searchQuery, setSearchQuery] = useState('');
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const [currentPage_, setCurrentPage_] = useState(1);
const [showLoadMore, setShowLoadMore] = useState(true);
const toggleCategory = (category: string) => {
setSelectedCategories(prev =>
prev.includes(category)
? prev.filter(c => c !== category)
: [...prev, category]
const [categoryId, setCategoryId] = useState(null)
const [page, setPage] = useState(1)
const [limit, setLimit] = useState(4)
const cityId = localStorage.getItem("cityId")
const { data, isLoading } = useGetSelectedCityOffersQuery({ cityId, categoryId, page, limit })
// optional chaining ensures no crash if data is undefined
const offers = data?.offers ?? [];
const categories = data?.categories ?? [];
const totalOffers = data?.paginationData.total ?? 0;
const totalPages = Math.ceil(totalOffers / limit);
const baseUrl = import.meta.env.VITE_BASE_URL;
if (isLoading) {
return (
<LoadingSpinner />
);
};
}
const filteredSavings = savingsData.filter(saving => {
const matchesSearch = saving.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
saving.business.toLowerCase().includes(searchQuery.toLowerCase());
const matchesCategory = selectedCategories.length === 0 || selectedCategories.includes(saving.category);
return matchesSearch && matchesCategory;
});
const itemsPerPage = 12;
const displayedSavings = filteredSavings.slice(0, currentPage_ * itemsPerPage);
const hasMoreItems = filteredSavings.length > displayedSavings.length;
const handleLoadMore = () => {
setCurrentPage_(prev => prev + 1);
if (!hasMoreItems) setShowLoadMore(false);
};
// Show different layouts based on login state
if (!user) {
// Not logged in - show marketing/landing page
return (
<Layout
activeCity="Melbourne"
@@ -378,19 +237,25 @@ export function SuperSavingsPage({
{/* Filter categories */}
<div className="space-y-4">
{filterCategories.map(category => (
<div key={category.value} className="flex items-center gap-3">
{categories.map((category: any) => (
<div key={category.id} className="flex items-center gap-3">
<Checkbox
id={category.value}
checked={selectedCategories.includes(category.value)}
onCheckedChange={() => toggleCategory(category.value)}
id={category.id}
checked={categoryId === category.id}
onCheckedChange={(checked: boolean) => {
if (checked) {
setCategoryId(category.id); // select this category
} else {
setCategoryId(null); // unselect if unchecked
}
}}
className="border-gray-400"
/>
<label
htmlFor={category.value}
htmlFor={category.id}
className="font-poppins text-sm text-gray-700 cursor-pointer flex-1"
>
{category.label} ({category.count})
{category.categoryName} ({category.offerCount})
</label>
</div>
))}
@@ -428,11 +293,11 @@ export function SuperSavingsPage({
</p>
</div>
{/* Savings Grid */}
{/* Offers Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-6 mb-16">
{displayedSavings.map((saving, index) => (
{offers.map((offer: any, index: number) => (
<motion.div
key={saving.id}
key={offer.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
@@ -441,17 +306,17 @@ export function SuperSavingsPage({
{/* Image */}
<div className="relative h-52 bg-gray-300">
<ImageWithFallback
src={saving.image}
alt={saving.title}
src={`${baseUrl}/${offer.websiteBannerImage}`}
alt={offer.title}
className="w-full h-full object-cover"
/>
<Button className="absolute bottom-4 right-3 bg-white rounded-full shadow-lg w-9 h-9 p-0 hover:bg-gray-100 transition-colors">
{/* <Button className="absolute bottom-4 right-3 bg-white rounded-full shadow-lg w-9 h-9 p-0 hover:bg-gray-100 transition-colors">
<Heart className="w-4 h-4 text-gray-800" />
</Button>
</Button> */}
{/* Discount Badge */}
<div className="absolute top-4 left-4 bg-primary text-white px-3 py-1.5 rounded-lg">
<span className="font-poppins font-semibold text-sm">{saving.discount}</span>
<span className="font-poppins font-semibold text-sm">{offer.offerCode}</span>
</div>
</div>
@@ -459,20 +324,20 @@ export function SuperSavingsPage({
{/* Business Name */}
<div className="flex items-center gap-2">
<div className="w-4 h-4 bg-gray-300 rounded"></div>
<span className="font-poppins text-sm text-gray-500">{saving.business}</span>
<span className="font-poppins text-sm text-gray-500">{offer.partnerName}</span>
</div>
{/* Title */}
<h3 className="font-poppins font-medium text-gray-900 leading-relaxed min-h-[48px]">
{saving.title}
{offer.description}
</h3>
{/* Saved Amount Display */}
<div className="bg-gradient-to-r from-primary/10 to-secondary/10 h-12 flex items-center justify-center rounded-lg">
<div className="flex items-center gap-2">
<Percent className="w-4 h-4 text-primary" />
{/* <Percent className="w-4 h-4 text-primary" /> */}
<span className="font-poppins font-semibold text-primary">
{saving.savedAmount}
{offer.title}
</span>
</div>
</div>
@@ -485,34 +350,39 @@ export function SuperSavingsPage({
{/* Minimal Pagination */}
<div className="flex justify-center py-8">
<div className="flex items-center gap-2">
{/* Previous button */}
<Button
variant="outline"
size="sm"
className="w-8 h-8 p-0 font-poppins"
disabled={currentPage_ === 1}
disabled={page === 1}
onClick={() => setPage(prev => Math.max(prev - 1, 1))}
>
<ChevronRight className="w-4 h-4 rotate-180" />
</Button>
{/* Page numbers */}
<div className="flex items-center gap-1">
{[1, 2, 3].map((page) => (
{Array.from({ length: totalPages }, (_, i) => i + 1).map(p => (
<Button
key={page}
variant={currentPage_ === page ? "default" : "ghost"}
key={p}
variant={page === p ? "default" : "ghost"}
size="sm"
className={`w-8 h-8 p-0 font-poppins ${currentPage_ === page ? 'bg-primary hover:bg-primary/90' : ''}`}
onClick={() => setCurrentPage_(page)}
className={`w-8 h-8 p-0 font-poppins ${page === p ? 'bg-primary hover:bg-primary/90' : ''}`}
onClick={() => setPage(p)}
>
{page}
{p}
</Button>
))}
</div>
{/* Next button */}
<Button
variant="outline"
size="sm"
className="w-8 h-8 p-0 font-poppins"
disabled={currentPage_ === 3}
disabled={page === totalPages}
onClick={() => setPage(prev => Math.min(prev + 1, totalPages))}
>
<ChevronRight className="w-4 h-4" />
</Button>
@@ -740,6 +610,6 @@ export function SuperSavingsPage({
</div>
</Layout>
);
}
}