From 5f108298fd1c7a1d4343c53298625ece2aee029a Mon Sep 17 00:00:00 2001 From: priyanshuvish Date: Fri, 20 Feb 2026 15:21:29 +0530 Subject: [PATCH 1/3] chnages --- src/components/MelbournePage.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/MelbournePage.tsx b/src/components/MelbournePage.tsx index 64abd05..abbf7c0 100644 --- a/src/components/MelbournePage.tsx +++ b/src/components/MelbournePage.tsx @@ -173,12 +173,9 @@ export function MelbournePage({ {/* Main Content */}
- {/* Sticky Page Navigation */} -
+ {/*
- {/* horizontal scroll wrapper */}
- {/* actual flex row */}
{[ { id: 'overview', label: 'Overview', icon: MapPin }, @@ -206,7 +203,7 @@ export function MelbournePage({
-
+
*/}
From 193fe3e722322fc13bc28b03139631632205bcb9 Mon Sep 17 00:00:00 2001 From: Hemant Vishwakarma Date: Fri, 20 Mar 2026 14:41:18 +0530 Subject: [PATCH 2/3] remove unused code --- src/components/AttractionDetailsPage.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/AttractionDetailsPage.tsx b/src/components/AttractionDetailsPage.tsx index d92c3b1..4962d84 100644 --- a/src/components/AttractionDetailsPage.tsx +++ b/src/components/AttractionDetailsPage.tsx @@ -1,13 +1,12 @@ -import { useState } from 'react'; +import { ArrowLeft, Calendar, Check, ChevronLeft, ChevronRight, Clock, MapPin, Users, X } from 'lucide-react'; import { motion } from 'motion/react'; -import { ArrowLeft, Clock, Users, Calendar, MapPin, Star, Check, X, ChevronLeft, ChevronRight } from 'lucide-react'; -import { Button } from './ui/button'; -import { Badge } from './ui/badge'; -import { Card, } from './ui/card'; -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { Layout } from '../Layout'; import { useParams } from 'react-router-dom'; +import { Layout } from '../Layout'; import { useGetAttractionDetailsByIdQuery } from '../Redux/services/attractions.service'; +import { ImageWithFallback } from './figma/ImageWithFallback'; +import { Badge } from './ui/badge'; +import { Button } from './ui/button'; +import { Card, } from './ui/card'; interface AttractionDetailsPageProps { onBackClick: () => void; From 27d73fa90c371e74f2eb88757f3ef78e65434b0c Mon Sep 17 00:00:00 2001 From: Hemant Vishwakarma Date: Fri, 17 Apr 2026 19:13:43 +0530 Subject: [PATCH 3/3] super savings details page added --- src/AppRouter.tsx | 8 + src/Redux/services/cities.service.ts | 8 +- src/pages/AttractionDetailsPage.tsx | 13 +- src/pages/SuperSavingsDetailsPage.tsx | 387 ++++++++++++++++++++++++++ src/pages/SuperSavingsPage.tsx | 5 +- 5 files changed, 407 insertions(+), 14 deletions(-) create mode 100644 src/pages/SuperSavingsDetailsPage.tsx diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx index 12945be..0683ff4 100644 --- a/src/AppRouter.tsx +++ b/src/AppRouter.tsx @@ -32,6 +32,7 @@ import { SuperSavingsPage } from './pages/SuperSavingsPage'; import { WhatsIncluded } from './pages/WhatsIncluded'; import { LandingMagicItineraryPage } from './pages/LandingMagicItineraryPage'; import { DiscoverPage } from './pages/DiscoverPage'; +import { SuperSavingsDetailsPage } from './pages/SuperSavingsDetailsPage'; // User type definition interface User { @@ -270,6 +271,13 @@ export function AppRouter({ } /> + + + navigate(-1)} /> + + } /> diff --git a/src/Redux/services/cities.service.ts b/src/Redux/services/cities.service.ts index acc5b31..c064904 100644 --- a/src/Redux/services/cities.service.ts +++ b/src/Redux/services/cities.service.ts @@ -41,7 +41,13 @@ export const citiesApi = createApi({ return `/website/super-savings/list/offers?${params.toString()}`; } }), + + getOfferDetailsById: builder.query({ + query: (id: number) => `/website/super-savings/list/offers/${id}`, + }), + + }), }); -export const { useGetCityListWithBannerQuery, useGetUpcomingCitiesQuery, useGetSelectedCityDetailsQuery, useGetSelectedCityOffersQuery } = citiesApi; \ No newline at end of file +export const { useGetCityListWithBannerQuery, useGetUpcomingCitiesQuery, useGetSelectedCityDetailsQuery, useGetSelectedCityOffersQuery, useGetOfferDetailsByIdQuery } = citiesApi; \ No newline at end of file diff --git a/src/pages/AttractionDetailsPage.tsx b/src/pages/AttractionDetailsPage.tsx index 64223b9..4c9f90c 100644 --- a/src/pages/AttractionDetailsPage.tsx +++ b/src/pages/AttractionDetailsPage.tsx @@ -1,25 +1,14 @@ -import { ArrowLeft, Calendar, Check, ChevronLeft, ChevronRight, Clock, MapPin, Users, X } from 'lucide-react'; +import { useState } from 'react'; import { motion } from 'motion/react'; -<<<<<<< HEAD:src/components/AttractionDetailsPage.tsx -======= import { ArrowLeft, Clock, Users, Calendar, MapPin, Star, Check, X, ChevronLeft, ChevronRight } from 'lucide-react'; import { Button } from '../components/ui/button'; import { Badge } from '../components/ui/badge'; import { Card, } from '../components/ui/card'; import { ImageWithFallback } from '../components/figma/ImageWithFallback'; import { Layout } from '../Layout'; ->>>>>>> cdadee5df40960af2263c2e5e6f4a8f234b7d5b0:src/pages/AttractionDetailsPage.tsx import { useParams } from 'react-router-dom'; -import { Layout } from '../Layout'; import { useGetAttractionDetailsByIdQuery } from '../Redux/services/attractions.service'; -<<<<<<< HEAD:src/components/AttractionDetailsPage.tsx -import { ImageWithFallback } from './figma/ImageWithFallback'; -import { Badge } from './ui/badge'; -import { Button } from './ui/button'; -import { Card, } from './ui/card'; -======= import LoadingSpinner from '../components/LoadingSpinner'; ->>>>>>> cdadee5df40960af2263c2e5e6f4a8f234b7d5b0:src/pages/AttractionDetailsPage.tsx interface AttractionDetailsPageProps { onBackClick: () => void; diff --git a/src/pages/SuperSavingsDetailsPage.tsx b/src/pages/SuperSavingsDetailsPage.tsx new file mode 100644 index 0000000..23c3988 --- /dev/null +++ b/src/pages/SuperSavingsDetailsPage.tsx @@ -0,0 +1,387 @@ +import { ArrowLeft, Check, Clock, MapPin, Users, X } from 'lucide-react'; +import { motion } from 'motion/react'; +import { useParams } from 'react-router-dom'; +import { ImageWithFallback } from '../components/figma/ImageWithFallback'; +import LoadingSpinner from '../components/LoadingSpinner'; +import { Badge } from '../components/ui/badge'; +import { Button } from '../components/ui/button'; +import { Card } from '../components/ui/card'; +import { Layout } from '../Layout'; +import { useGetOfferDetailsByIdQuery } from '../Redux/services/cities.service'; + +interface SuperSavingsDetailsPageProps { + onBackClick: () => void; + onCheckoutClick: () => void; + onSignInClick: () => void; + onSignOutClick?: () => void; + user?: { email: string; name: string } | null; +} + +export function SuperSavingsDetailsPage({ + onBackClick, + onCheckoutClick, + onSignInClick, + onSignOutClick, + user, +}: SuperSavingsDetailsPageProps) { + const { id } = useParams(); + const { data: offer, isLoading } = useGetOfferDetailsByIdQuery(Number(id)); + const baseUrl = import.meta.env.VITE_BASE_URL; + + + if (isLoading) { + return ; + } + + // Guard against missing data – but keep all UI elements + const safeOffer = offer || { + id: 0, + title: 'Offer Details', + description: 'No description available.', + cityXid: 0, + cardXid: 0, + cardTypeXid: 0, + categoryXid: 0, + partnerName: '', + offerCode: '', + websiteBannerImage: '', + mobileBannerImage: '', + redemptionLink: '', + passType: '', + startDateTime: null, + endDateTime: null, + applyToPasses: false, + stepsForBooking: null, + offerStatus: '', + isActive: true, + createdAt: '', + updatedAt: '', + city: { id: 0, cityName: 'Unknown City' }, + card: { id: 0, title: 'Unknown Card' }, + cardType: { id: 0, cardTypeDisplayName: 'Standard' }, + category: { id: 0, categoryName: 'General' }, + }; + + // Build badges from available API data (preserves the badge UI section) + const superSavingsBadges = [ + safeOffer.category && { badgeXid: safeOffer.category.id, badge: { badgeName: safeOffer.category.categoryName } }, + safeOffer.cardType && { badgeXid: safeOffer.cardType.id, badge: { badgeName: safeOffer.cardType.cardTypeDisplayName } }, + safeOffer.offerCode && { badgeXid: -1, badge: { badgeName: `Code: ${safeOffer.offerCode}` } }, + safeOffer.offerStatus && { badgeXid: -2, badge: { badgeName: safeOffer.offerStatus.toUpperCase() } }, + ].filter(Boolean); + + // Build gallery array from banner images (original expected superSavingsGalleries) + const superSavingsGalleries = []; + if (safeOffer.websiteBannerImage) { + superSavingsGalleries.push({ id: 1, filePathUrl: safeOffer.websiteBannerImage }); + } + if (safeOffer.mobileBannerImage) { + superSavingsGalleries.push({ id: 2, filePathUrl: safeOffer.mobileBannerImage }); + } + // If no images, add a placeholder + if (superSavingsGalleries.length === 0) { + superSavingsGalleries.push({ id: 0, filePathUrl: 'https://placehold.co/1200x800?text=No+Image' }); + } + + // Mock data for sections not present in API (preserve structure but show empty/fallback) + const durations = safeOffer.startDateTime && safeOffer.endDateTime + ? Math.round((new Date(safeOffer.endDateTime).getTime() - new Date(safeOffer.startDateTime).getTime()) / (1000 * 60)) + : 'Not specified'; + const groupSize = 'Not specified'; + const ageRange = 'All ages'; + const superSavingsLanguages: any[] = []; // API has no language data + const superSavingsHighlights: any[] = []; // API has no highlights + // Inclusions: API has none, so show empty state (or we could derive from redemptionLink etc.) + const superSavingsInclusions: any[] = []; + const address = safeOffer.city?.cityName || 'Location not specified'; + + return ( + +
+ {/* Back Button */} + + + + + {/* Title and Badges Section */} +
+
+ {superSavingsBadges.map((badge: any, index: number) => ( + + {badge.badge.badgeName} + + ))} +
+ +

+ + {safeOffer.title} + {' '} + + Day Trip by {safeOffer.partnerName || safeOffer.card?.title || 'Partner'} + +

+
+ + {/* Image Gallery Section - preserved exactly as original */} +
+ {/* Main large image */} +
+ +
+ + {/* Gallery images - use remaining images or repeat first if needed */} + {superSavingsGalleries.slice(1, 5).map((image: any) => ( +
+ +
+ ))} + {/* If less than 4 extra images, fill with placeholders to maintain grid */} + {superSavingsGalleries.slice(1, 5).length < 4 && + Array(4 - superSavingsGalleries.slice(1, 5).length) + .fill(null) + .map((_, idx) => ( +
+
+ No Image +
+
+ ))} +
+ + {/* Main Content Grid */} +
+ {/* Left Content - Tour Details */} +
+ {/* Overview Cards - preserved */} +
+ {/* Duration */} + +
+ +
+

Duration

+

+ {typeof durations === 'number' ? `${durations} mins` : durations} +

+
+ + {/* Group Size */} + +
+ +
+

Group Size

+

{groupSize}

+
+ + {/* Age Range */} + +
+ +
+

Age Range

+

{ageRange}

+
+ + {/* Languages */} + +
+ +
+

Languages

+

+ {superSavingsLanguages?.length > 0 + ? superSavingsLanguages?.map((lang: any) => lang.language.name).join(', ') + : 'English (default)'} +

+
+
+ + {/* Tour Overview */} +
+
+
+

+ Tour Overview +

+
+

+ {safeOffer.description} +

+
+ + {/* Tour Highlights - preserved even if empty */} +
+
+
+

+ Tour Highlights +

+
+ {superSavingsHighlights.length > 0 ? ( +
    + {superSavingsHighlights.map((highlight: any) => ( +
  • +
    +
    +
    + {highlight.title} +
  • + ))} +
+ ) : ( +

No highlights listed for this offer.

+ )} +
+ + {/* What's Included/Not Included - preserved */} +
+
+
+

+ What's included +

+
+
+ {/* Included */} +
+

+ + Included +

+ {superSavingsInclusions.filter((inc: any) => inc.isInclusion === true).length > 0 ? ( + superSavingsInclusions + .filter((inclusion: any) => inclusion.isInclusion === true) + .map((inclusion: any) => ( +
+
+ +
+ {inclusion.title} +
+ )) + ) : ( +

No included items specified.

+ )} +
+ + {/* Not Included */} +
+

+ + Not Included +

+ {superSavingsInclusions.filter((inc: any) => inc.isInclusion === false).length > 0 ? ( + superSavingsInclusions + .filter((inclusion: any) => inclusion.isInclusion === false) + .map((inclusion: any) => ( +
+
+ +
+ {inclusion.title} +
+ )) + ) : ( +

No excluded items specified.

+ )} +
+
+
+ + {/* Location on map - preserved */} +
+
+
+

+ Location on map +

+
+
+
+
+ +
+

Interactive Map

+

{safeOffer.title}

+

{address}

+
+
+
+
+ + {/* Right Sidebar - Calendar and Booking (preserved, but you can add a real calendar if needed) */} +
+ +

Book This Offer

+
+
+ Availability + + {safeOffer.offerStatus === 'active' ? 'Available' : 'Unavailable'} + +
+ {safeOffer.startDateTime && ( +
+ Valid from + + {new Date(safeOffer.startDateTime).toLocaleDateString()} + +
+ )} + {safeOffer.endDateTime && ( +
+ Valid until + + {new Date(safeOffer.endDateTime).toLocaleDateString()} + +
+ )} +
+ +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/pages/SuperSavingsPage.tsx b/src/pages/SuperSavingsPage.tsx index 8b9e680..40be9f3 100644 --- a/src/pages/SuperSavingsPage.tsx +++ b/src/pages/SuperSavingsPage.tsx @@ -17,6 +17,7 @@ import { TrustedCompanies } from '../components/TrustedCompanies'; import { Layout } from '../Layout'; import { useGetSelectedCityOffersQuery } from '../Redux/services/cities.service'; import LoadingSpinner from '../components/LoadingSpinner'; +import { useNavigate } from 'react-router-dom'; interface SuperSavingsPageProps { onBackClick: () => void; @@ -113,6 +114,7 @@ export function SuperSavingsPage({ user }: SuperSavingsPageProps) { + const navigate = useNavigate(); const [categoryId, setCategoryId] = useState(null) const [page, setPage] = useState(1) const [limit, setLimit] = useState(4) @@ -302,7 +304,8 @@ export function SuperSavingsPage({ animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: index * 0.1 }} > - + navigate(`/super-savings/${offer.id}`)}> {/* Image */}