integrate api in viewIternary page

This commit is contained in:
aryabenade
2026-04-22 18:33:09 +05:30
parent adbf30a0c2
commit beae316bc0
4 changed files with 200 additions and 438 deletions

View File

@@ -222,7 +222,7 @@ export function AppRouter({
<ItineraryViewPage {...commonNavHandlers} />
</motion.div>
} />
<Route path="/itinerary-view-design" element={
<Route path="/itinerary-view-design/:itineraryId" element={
<motion.div key="itinerary-view" {...pageTransition}>
<ItineraryViewPageDesign {...commonNavHandlers} />
</motion.div>

View File

@@ -23,6 +23,9 @@ export const itineraryApi = createApi({
query: (itineraryId: number) => `/website/itinerary/${itineraryId}`,
}),
getUserItineraries: builder.query({
query: () => `/website/itinerary/all-initineraries`,
}),
})
});
@@ -30,4 +33,5 @@ export const itineraryApi = createApi({
export const {
useCreateMagicItineraryMutation,
useGetItineraryDetailsByIdQuery,
useGetUserItinerariesQuery
} = itineraryApi;

View File

@@ -7,6 +7,9 @@ import { Badge } from '../components/ui/badge';
import Navbar from '../components/Navbar';
import { Footer } from '../components/Footer';
import { ImageWithFallback } from '../components/figma/ImageWithFallback';
import { useGetItineraryDetailsByIdQuery } from '../Redux/services/itinerary.service';
import { useParams } from 'react-router-dom';
import LoadingSpinner from '../components/LoadingSpinner';
interface ItineraryViewPageDesignProps {
onBackClick: () => void;
@@ -35,18 +38,6 @@ interface ItineraryViewPageDesignProps {
user?: { email: string; name: string; } | null;
}
// Enhanced activity type with more details
interface Activity {
time: string;
activity: string;
location: string;
address: string;
image: string;
categories: string[];
description: string[];
isFavorite?: boolean;
}
export function ItineraryViewPageDesign({
onBackClick,
onHomeClick,
@@ -74,304 +65,36 @@ export function ItineraryViewPageDesign({
user
}: ItineraryViewPageDesignProps) {
const [viewMode, setViewMode] = useState<'daily' | 'summary'>('daily');
const [favorites, setFavorites] = useState<Set<string>>(new Set());
// const [favorites, setFavorites] = useState<Set<string>>(new Set());
const toggleFavorite = (activityKey: string) => {
setFavorites(prev => {
const newSet = new Set(prev);
if (newSet.has(activityKey)) {
newSet.delete(activityKey);
} else {
newSet.add(activityKey);
}
return newSet;
});
};
// ── API Integration ──────────────────────────────────────────────────────────
const { itineraryId } = useParams();
const { data: itineraryDetails, isLoading } = useGetItineraryDetailsByIdQuery(itineraryId);
// Enhanced itinerary data with images, addresses, and detailed info
const generatedItinerary = {
destination: {
name: 'Melbourne',
country: 'Australia',
weather: '18°C, Sunny',
image: 'https://images.unsplash.com/photo-1514395462725-fb4566210144?w=400&h=300&fit=crop'
},
totalDays: 3,
estimatedCost: '$450 AUD',
includedActivities: 18,
dailyPlans: [
{
day: 1,
title: "City Center & Culture",
activities: [
{
time: '8:00 am',
activity: 'The Langham Melbourne',
location: 'The Langham Melbourne',
address: '1 Southgate Avenue, Southbank VIC 3006',
image: 'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=800&h=600&fit=crop',
categories: ['Accommodation', 'Luxury'],
description: [
'Check-in at luxury riverside hotel',
'Enjoy complimentary breakfast',
'Relax at the spa facilities',
'Explore the surrounding Southbank area'
]
},
{
time: '10:00 am',
activity: 'Federation Square',
location: 'Federation Square',
address: 'Corner Swanston & Flinders Streets, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1514395462725-fb4566210144?w=800&h=600&fit=crop',
categories: ['Culture', 'Landmark'],
description: [
'Explore Melbourne\'s cultural precinct',
'Visit the ACMI museum',
'Enjoy street performances',
'Take photos at iconic locations'
]
},
{
time: '12:00 pm',
activity: 'Degrave Street Café',
location: 'Degrave Street Espresso Bar',
address: '23-25 Degraves Street, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1554118811-1e0d58224f24?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks', 'Culture'],
description: [
'Coffee at Pellegrini\'s Espresso Bar (iconic old-school cafe)',
'Try the famous jam doughnuts',
'Shop for fresh produce in the Deli Hall',
'Pick up unique souvenirs in the General Merchandise section'
]
},
{
time: '2:00 pm',
activity: 'Royal Botanic Gardens',
location: 'Royal Botanic Gardens Victoria',
address: 'Birdwood Avenue, South Yarra VIC 3141',
image: 'https://images.unsplash.com/photo-1585320806297-9794b3e4eeae?w=800&h=600&fit=crop',
categories: ['Nature', 'Culture'],
description: [
'Stroll through stunning landscaped gardens',
'Visit the Australian Forest Walk',
'Relax by the Ornamental Lake',
'Join a free guided walking tour'
]
},
{
time: '4:00 pm',
activity: 'National Gallery of Victoria',
location: 'NGV International',
address: '180 St Kilda Road, Melbourne VIC 3006',
image: 'https://images.unsplash.com/photo-1564399577149-749794d74eee?w=800&h=600&fit=crop',
categories: ['Culture', 'Art'],
description: [
'Explore Australia\'s oldest art museum',
'View international and Australian art collections',
'Visit the stunning water wall entrance',
'Browse the NGV design store'
]
},
{
time: '7:00 pm',
activity: 'Dinner at Chin Chin',
location: 'Chin Chin Restaurant',
address: '125 Flinders Lane, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1552566626-52f8b828add9?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks'],
description: [
'Experience modern Thai cuisine',
'Try signature dishes like the Betel Leaf',
'Enjoy the vibrant atmosphere',
'Book ahead or walk-in for bar seating'
]
}
]
},
{
day: 2,
title: "Markets & Neighborhoods",
activities: [
{
time: '8:00 am',
activity: 'Queen Victoria Market',
location: 'Queen Victoria Market',
address: 'Queen Street, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1555939594-58d7cb561ad1?w=800&h=600&fit=crop',
categories: ['Food', 'Shopping', 'Culture'],
description: [
'Explore Melbourne\'s historic market (since 1878)',
'Sample fresh local produce',
'Shop for artisan goods and souvenirs',
'Grab breakfast at the Deli Hall'
]
},
{
time: '10:30 am',
activity: 'Fitzroy Street Art Tour',
location: 'Fitzroy Arts Precinct',
address: 'Gertrude Street, Fitzroy VIC 3065',
image: 'https://images.unsplash.com/photo-1499781350541-7783f6c6a0c8?w=800&h=600&fit=crop',
categories: ['Culture', 'Art'],
description: [
'Walk through famous street art laneways',
'Discover works by renowned artists',
'Visit independent galleries',
'Explore vintage and record stores'
]
},
{
time: '12:30 pm',
activity: 'Brunswick Street Lunch',
location: 'Brunswick Street Precinct',
address: 'Brunswick Street, Fitzroy VIC 3065',
image: 'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks'],
description: [
'Choose from diverse dining options',
'Try local cafes and restaurants',
'Explore bookshops and boutiques',
'Enjoy the vibrant neighborhood atmosphere'
]
},
{
time: '2:30 pm',
activity: 'Carlton Gardens',
location: 'Carlton Gardens',
address: 'Carlton Gardens, Carlton VIC 3053',
image: 'https://images.unsplash.com/photo-1519331379826-f10be5486c6f?w=800&h=600&fit=crop',
categories: ['Nature', 'Culture', 'Landmark'],
description: [
'Visit the UNESCO World Heritage site',
'See the Royal Exhibition Building',
'Stroll through Victorian-era gardens',
'Relax by the ornamental fountains'
]
},
{
time: '4:00 pm',
activity: 'Melbourne Museum',
location: 'Melbourne Museum',
address: '11 Nicholson Street, Carlton VIC 3053',
image: 'https://images.unsplash.com/photo-1566127992631-137a642a90f4?w=800&h=600&fit=crop',
categories: ['Culture', 'Museum'],
description: [
'Explore natural and cultural history',
'Visit the Bunjilaka Aboriginal Centre',
'See the Forest Gallery living ecosystem',
'Discover Melbourne\'s story exhibition'
]
},
{
time: '7:00 pm',
activity: 'Rooftop Bar Experience',
location: 'Naked for Satan',
address: '285 Brunswick Street, Fitzroy VIC 3065',
image: 'https://images.unsplash.com/photo-1514933651103-005eec06c04b?w=800&h=600&fit=crop',
categories: ['Drinks', 'Food'],
description: [
'Enjoy sunset views from the rooftop',
'Try Spanish-style pintxos',
'Sample craft cocktails and local beers',
'Experience Melbourne\'s bar culture'
]
}
]
},
{
day: 3,
title: "Coastal Adventure",
activities: [
{
time: '8:00 am',
activity: 'St. Kilda Beach',
location: 'St. Kilda Beach',
address: 'Jacka Boulevard, St Kilda VIC 3182',
image: 'https://images.unsplash.com/photo-1505142468610-359e7d316be0?w=800&h=600&fit=crop',
categories: ['Nature', 'Beach'],
description: [
'Morning walk along the iconic beach',
'Visit the historic St Kilda Pier',
'See the little penguins at sunset',
'Explore the Sunday Esplanade Market (weekends)'
]
},
{
time: '10:00 am',
activity: 'Acland Street Cafes',
location: 'Acland Street',
address: 'Acland Street, St Kilda VIC 3182',
image: 'https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks'],
description: [
'Brunch at famous cake shops',
'Try traditional European pastries',
'Visit Lentil as Anything (pay-as-you-feel)',
'Browse vintage shops and bookstores'
]
},
{
time: '12:00 pm',
activity: 'Luna Park Melbourne',
location: 'Luna Park Melbourne',
address: '18 Lower Esplanade, St Kilda VIC 3182',
image: 'https://images.unsplash.com/photo-1513026705753-bc3fffca8bf4?w=800&h=600&fit=crop',
categories: ['Entertainment', 'Landmark'],
description: [
'Visit Melbourne\'s iconic amusement park',
'Ride the historic Scenic Railway (1912)',
'Take photos at Mr Moon entrance',
'Enjoy carnival games and rides'
]
},
{
time: '2:00 pm',
activity: 'Brighton Beach Boxes',
location: 'Brighton Beach',
address: 'Esplanade, Brighton VIC 3186',
image: 'https://images.unsplash.com/photo-1520208422220-d12a3c588e6c?w=800&h=600&fit=crop',
categories: ['Culture', 'Landmark'],
description: [
'Photograph the famous colorful bathing boxes',
'Walk along the pristine beach',
'Learn about the heritage structures',
'Relax in the beachside atmosphere'
]
},
{
time: '4:00 pm',
activity: 'Southbank Promenade',
location: 'Southbank',
address: 'Southbank Promenade, Southbank VIC 3006',
image: 'https://images.unsplash.com/photo-1559827260-dc66d52bef19?w=800&h=600&fit=crop',
categories: ['Culture', 'Shopping'],
description: [
'Stroll along the Yarra River',
'Visit arts and craft markets',
'Explore restaurants and cafes',
'Enjoy river views and street performers'
]
},
{
time: '7:00 pm',
activity: 'Farewell Dinner at Vue de Monde',
location: 'Vue de Monde',
address: 'Level 55, Rialto, 525 Collins Street, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks', 'Luxury'],
description: [
'Experience fine dining at 55th floor',
'Enjoy panoramic Melbourne views',
'Taste modern Australian cuisine',
'Celebrate the end of your journey'
]
}
]
}
]
};
const generatedItinerary = itineraryDetails ?? null;
const days = generatedItinerary?.days ?? [];
const summaries = generatedItinerary?.summary ?? [];
// ─────────────────────────────────────────────────────────────────────────────
// const toggleFavorite = (activityKey: string) => {
// setFavorites(prev => {
// const newSet = new Set(prev);
// if (newSet.has(activityKey)) {
// newSet.delete(activityKey);
// } else {
// newSet.add(activityKey);
// }
// return newSet;
// });
// };
// ── Loading State ─────────────────────────────────────────────────────────────
if (isLoading) {
return (
<LoadingSpinner/>
);
}
// ─────────────────────────────────────────────────────────────────────────────
return (
<div className="min-h-screen bg-background">
@@ -422,12 +145,14 @@ export function ItineraryViewPageDesign({
<Star className="w-6 h-6 fill-current" />
<h1 className="font-merchant text-4xl md:text-5xl lg:text-6xl leading-tight">
<span className="font-light">Your</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">Magic Itinerary</span>
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pr-3">
{generatedItinerary?.title ?? 'Magic Itinerary'}
</span>
</h1>
<Star className="w-6 h-6 fill-current" />
</div>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-600 max-w-2xl mx-auto">
Here's your personalized {generatedItinerary.totalDays}-day adventure in {generatedItinerary.destination.name}!
Here's your personalized {generatedItinerary?.totalDays}-day adventure in {generatedItinerary?.city}!
</p>
</motion.div>
</div>
@@ -437,6 +162,7 @@ export function ItineraryViewPageDesign({
<section className="py-8">
<div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto space-y-8">
{/* View Toggle */}
<div className="flex justify-center">
<div className="bg-muted p-1 rounded-lg">
@@ -459,31 +185,46 @@ export function ItineraryViewPageDesign({
</div>
</div>
{/* Itinerary Overview */}
{/* Itinerary Overview Card */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
>
<Card className="p-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="text-center">
<div className="font-merchant text-3xl text-primary mb-2">{generatedItinerary.totalDays}</div>
<div className="font-poppins text-sm text-muted-foreground font-normal">Days</div>
{/* Banner Image */}
<Card className="overflow-hidden">
<div className="relative h-40 md:h-48">
<ImageWithFallback
src={generatedItinerary?.cityBanner}
alt={generatedItinerary?.city}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent" />
<div className="absolute bottom-4 left-6">
<p className="font-poppins text-xs font-medium text-white/70 uppercase tracking-wider mb-1">Your Trip</p>
<h3 className="font-merchant text-2xl md:text-3xl text-white font-semibold">{generatedItinerary?.city}</h3>
</div>
<div className="text-center">
<div className="font-merchant text-3xl text-primary mb-2">{generatedItinerary.includedActivities}</div>
<div className="font-poppins text-sm text-muted-foreground font-normal">Activities</div>
</div>
{/* Stats Row */}
<div className="grid grid-cols-3 divide-x divide-gray-100 bg-white">
<div className="flex flex-col items-center py-5">
<span className="font-merchant text-3xl text-primary">{generatedItinerary?.totalDays}</span>
<span className="font-poppins text-xs font-normal text-gray-500 mt-1">Days</span>
</div>
<div className="text-center">
<div className="font-merchant text-3xl text-primary mb-2">{generatedItinerary.estimatedCost}</div>
<div className="font-poppins text-sm text-muted-foreground font-normal">Estimated Cost</div>
<div className="flex flex-col items-center py-5">
<span className="font-merchant text-3xl text-primary">{generatedItinerary?.totalStops}</span>
<span className="font-poppins text-xs font-normal text-gray-500 mt-1">Stops</span>
</div>
<div className="flex flex-col items-center py-5">
<span className="font-merchant text-3xl text-primary">{days[0]?.date}</span>
<span className="font-poppins text-xs font-normal text-gray-500 mt-1">Start Date</span>
</div>
</div>
</Card>
</motion.div>
{/* Daily Plans - Enhanced View */}
{/* ── Daily View ──────────────────────────────────────────────────── */}
{viewMode === 'daily' && (
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -491,43 +232,24 @@ export function ItineraryViewPageDesign({
transition={{ duration: 0.6, delay: 0.3 }}
className="space-y-12"
>
{generatedItinerary.dailyPlans.map((day, dayIndex) => (
{days.map((day: any, dayIndex: number) => (
<div key={dayIndex} className="space-y-6">
{/* Location Header with Weather - Only show for first day or when city actually changes */}
{(() => {
// Get current day's destination (fallback to main destination if not specified)
const currentDestination = (day as any).destination || generatedItinerary.destination;
// Check if this is the first day
if (dayIndex === 0) return true;
// Check if destination changed from previous day
if (dayIndex > 0) {
const previousDay = generatedItinerary.dailyPlans[dayIndex - 1];
const previousDestination = (previousDay as any).destination || generatedItinerary.destination;
// Only show if city name is different
return currentDestination.name !== previousDestination.name;
}
return false;
})() && (
{/* City / Weather header — only on first day */}
{dayIndex === 0 && (
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.4 + dayIndex * 0.1 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="bg-gray-50 rounded-2xl p-6 shadow-sm border border-gray-200"
>
<div className="flex items-center justify-between">
<div>
<h2 className="font-poppins text-3xl md:text-4xl leading-tight mb-2">
{((day as any).destination || generatedItinerary.destination).name}, {((day as any).destination || generatedItinerary.destination).country}
{generatedItinerary?.city}, Australia
</h2>
<p className="font-poppins text-base text-primary font-medium">{((day as any).destination || generatedItinerary.destination).weather}</p>
</div>
<div className="flex items-center gap-2">
<Sun className="w-10 h-10 text-amber-500" />
</div>
<Sun className="w-10 h-10 text-amber-500" />
</div>
</motion.div>
)}
@@ -540,24 +262,28 @@ export function ItineraryViewPageDesign({
className="flex items-center gap-4 pl-2"
>
<div className="bg-gradient-to-br from-primary to-secondary text-white w-16 h-16 rounded-full flex items-center justify-center shadow-lg">
<span className="font-merchant text-2xl font-semibold">{day.day}</span>
<span className="font-merchant text-2xl font-semibold">{day.dayNumber}</span>
</div>
<div>
<h3 className="font-merchant text-2xl md:text-3xl leading-snug font-semibold">Day {day.day}</h3>
<h3 className="font-merchant text-2xl md:text-3xl leading-snug font-semibold">
Day {day.dayNumber}
</h3>
<p className="font-poppins text-base text-muted-foreground font-normal">{day.title}</p>
</div>
</motion.div>
{/* GMT Label */}
{/* Time-zone label */}
<div className="pl-2">
<p className="font-poppins text-sm text-gray-500 font-normal">GMT</p>
<p className="font-poppins text-sm text-gray-500 font-normal">
{day.date}
</p>
</div>
{/* Activity Cards - Desktop Grid Layout */}
{/* Activity Cards */}
<div className="space-y-8">
{day.activities.map((activity, actIndex) => {
const activityKey = `day${day.day}-act${actIndex}`;
const isFavorite = favorites.has(activityKey);
{day.items?.map((activity: any, actIndex: number) => {
const activityKey = `day${day.dayNumber}-act${actIndex}`;
// const isFavorite = favorites.has(activityKey);
return (
<motion.div
@@ -569,23 +295,25 @@ export function ItineraryViewPageDesign({
>
{/* Time Column */}
<div className="flex-shrink-0 w-24 pt-2">
<div className="font-poppins text-base font-medium text-gray-700">{activity.time}</div>
<div className="font-poppins text-base font-medium text-gray-700">
{activity.timeSlot}
</div>
</div>
{/* Activity Card */}
<div className="flex-1">
<Card className="overflow-hidden hover:shadow-xl transition-shadow duration-300 border-2 border-gray-100">
<CardContent className="p-0">
{/* Hero Image with Overlay Buttons */}
{/* Hero Image */}
<div className="relative h-64 md:h-72 bg-gray-200">
<ImageWithFallback
src={activity.image}
alt={activity.activity}
src={activity.imageUrl}
alt={activity.title}
className="w-full h-full object-cover"
/>
{/* Favorite Heart Button - Top Right */}
<div className="absolute top-4 right-4">
{/* Favourite Button */}
{/* <div className="absolute top-4 right-4">
<Button
size="icon"
variant="secondary"
@@ -594,12 +322,18 @@ export function ItineraryViewPageDesign({
>
<Heart className={`w-5 h-5 ${isFavorite ? 'fill-primary text-primary' : 'text-gray-700'}`} />
</Button>
</div>
</div> */}
{/* Get Directions Button - Bottom Left */}
{/* Get Directions — links to Google Maps via lat/lng */}
<div className="absolute bottom-4 left-4">
<Button
<Button
className="bg-primary hover:bg-primary/90 text-white font-poppins font-semibold shadow-lg px-6 py-3 rounded-xl"
onClick={() =>
window.open(
`https://www.google.com/maps?q=${activity.latitude},${activity.longitude}`,
'_blank'
)
}
>
<Navigation className="w-4 h-4 mr-2" />
Get Directions
@@ -607,40 +341,41 @@ export function ItineraryViewPageDesign({
</div>
</div>
{/* Content Section */}
{/* Content */}
<div className="p-6 space-y-4">
{/* Location Name & Address */}
<div className="space-y-2">
<h4 className="font-merchant text-xl md:text-2xl leading-snug font-semibold text-gray-900">
{activity.activity}
{activity.title}
</h4>
<div className="flex items-start gap-2 text-gray-600">
<MapPin className="w-4 h-4 mt-1 flex-shrink-0 text-primary" />
<span className="font-poppins text-sm font-normal leading-relaxed">{activity.address}</span>
<span className="font-poppins text-sm font-normal leading-relaxed">
{activity.locationName}
</span>
</div>
</div>
{/* Category Badges */}
<div className="flex flex-wrap gap-2">
{activity.categories.map((category, catIndex) => (
<Badge
key={catIndex}
{activity.categories?.map((cat: string, ci: number) => (
<Badge
key={ci}
variant="secondary"
className="font-poppins font-normal text-sm bg-primary/10 text-primary hover:bg-primary/20 px-3 py-1"
>
{category}
{cat}
</Badge>
))}
</div>
{/* Activity Details - Bullet Points */}
{/* Description */}
<div className="space-y-2 pt-2">
{activity.description.map((detail, detailIndex) => (
<div key={detailIndex} className="flex items-start gap-3">
<span className="text-primary font-semibold mt-1.5 flex-shrink-0"></span>
<span className="font-poppins text-sm font-normal text-gray-600 leading-relaxed">{detail}</span>
</div>
))}
<div className="flex items-center gap-3">
<span className="text-primary font-semibold mt-1 flex-shrink-0">•</span>
<span className="font-poppins text-sm font-normal text-gray-600 leading-relaxed">
{activity.description}
</span>
</div>
</div>
</div>
</CardContent>
@@ -655,7 +390,7 @@ export function ItineraryViewPageDesign({
</motion.div>
)}
{/* Summary View */}
{/* ── Summary View ─────────────────────────────────────────────────── */}
{viewMode === 'summary' && (
<motion.div
initial={{ opacity: 0, y: 20 }}
@@ -663,32 +398,57 @@ export function ItineraryViewPageDesign({
transition={{ duration: 0.6, delay: 0.3 }}
className="space-y-4"
>
<h3 className="font-merchant text-2xl md:text-3xl text-center mb-8 leading-tight font-semibold">Trip Summary</h3>
<h3 className="font-merchant text-2xl md:text-3xl text-center mb-8 leading-tight font-semibold">
Trip Summary
</h3>
<Card className="p-6">
<div className="space-y-6">
{generatedItinerary.dailyPlans.map((day, index) => (
<div key={index} className="border-l-4 border-primary pl-6">
<div className="flex items-center gap-2 mb-3">
<Calendar className="w-5 h-5 text-primary" />
<h4 className="font-merchant text-lg md:text-xl leading-snug font-semibold">Day {day.day}: {day.title}</h4>
</div>
<div className="space-y-2">
{day.activities.map((activity, actIndex) => (
<div key={actIndex} className="flex items-start gap-3 text-sm">
<CheckCircle className="w-4 h-4 text-green-500 flex-shrink-0 mt-0.5" />
<div className="flex-1">
<span className="font-poppins text-gray-700 font-medium">{activity.activity}</span>
<div className="flex items-center gap-2 mt-1">
<span className="font-poppins text-gray-500 text-xs font-normal">{activity.time}</span>
<span className="text-gray-400"></span>
<span className="font-poppins text-gray-500 text-xs font-normal">{activity.location}</span>
{days.map((day: any, dayIndex: number) => {
// ✅ Match summary to the correct day by dayNumber
const daySummary = summaries.find((s: any) => s.dayNumber === day.dayNumber);
const dayDate = days[0]?.date
? new Date(
new Date(days[0].date).setDate(
new Date(days[0].date).getDate() + dayIndex
)
).toLocaleDateString('en-AU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
})
: '';
return (
<div key={dayIndex} className="border-l-4 border-primary pl-6">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<Calendar className="w-5 h-5 text-primary" />
<h4 className="font-merchant text-lg md:text-xl leading-snug font-semibold">
Day {day.dayNumber}: {daySummary?.title ?? day.title}
</h4>
</div>
<span className="font-poppins text-sm text-primary font-medium">{dayDate}</span>
</div>
<div className="space-y-2">
{daySummary?.items?.map((item: any, actIndex: number) => (
<div key={actIndex} className="flex items-start gap-3 text-sm">
<CheckCircle className="w-4 h-4 text-green-500 flex-shrink-0 mt-0.5" />
<div className="flex-1">
<span className="font-poppins text-gray-700 font-medium">{item.title}</span>
<div className="flex items-center gap-2 mt-1">
<span className="font-poppins text-gray-500 text-xs font-normal">{item.timeSlot}</span>
<span className="text-gray-400"></span>
<span className="font-poppins text-gray-500 text-xs font-normal">{item.locationName}</span>
</div>
</div>
</div>
</div>
))}
))}
</div>
</div>
</div>
))}
);
})}
</div>
</Card>
</motion.div>
@@ -709,16 +469,11 @@ export function ItineraryViewPageDesign({
<Heart className="w-5 h-5 mr-2" />
Create Another
</Button>
<Button
className="bg-primary hover:bg-primary/90 font-poppins font-semibold px-8 py-3 text-lg"
>
<Button className="bg-primary hover:bg-primary/90 font-poppins font-semibold px-8 py-3 text-lg">
<Download className="w-5 h-5 mr-2" />
Save Itinerary
</Button>
<Button
variant="outline"
className="font-poppins font-medium px-8 py-3 text-lg"
>
<Button variant="outline" className="font-poppins font-medium px-8 py-3 text-lg">
<Share2 className="w-5 h-5 mr-2" />
Share Trip
</Button>
@@ -728,7 +483,7 @@ export function ItineraryViewPageDesign({
</section>
{/* Footer */}
<Footer
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}

View File

@@ -30,6 +30,7 @@ import { useGetUserCardsQuery, useGetUserProfileDetailsQuery, useUpdateUserProfi
import { toast } from 'sonner';
import { useNavigate } from 'react-router-dom';
import LoadingSpinner from '../components/LoadingSpinner';
import { useGetUserItinerariesQuery } from '../Redux/services/itinerary.service';
interface ProfilePageProps {
onBackClick: () => void;
@@ -170,8 +171,10 @@ export function ProfilePage({
const { data: userDetails, isLoading } = useGetUserProfileDetailsQuery(userId)
const [updateUserProfileDetails, { isLoading: savingChanges }] = useUpdateUserProfileDetailsMutation();
const { data, isLoading: loadingCards } = useGetUserCardsQuery(sort)
const { data: userItineraries, isLoading: loadingItineraries } = useGetUserItinerariesQuery({})
const cards = data ?? []
const itineraries = userItineraries?.itineraries ?? []
useEffect(() => {
if (userDetails) {
@@ -427,8 +430,8 @@ export function ProfilePage({
<CardContent className="p-8 space-y-6">
{(() => {
// Determine which pass type to show
const hasUnlimitedPass = activeCards.some((card:any) => card.cardType.cardTypeName === 'selective_pass');
const hasSelectivePass = activeCards.some((card:any) => card.cardType.cardTypeName === 'unlimited_card');
const hasUnlimitedPass = activeCards.some((card: any) => card.cardType.cardTypeName === 'selective_pass');
const hasSelectivePass = activeCards.some((card: any) => card.cardType.cardTypeName === 'unlimited_card');
if (hasUnlimitedPass) {
return (
@@ -681,7 +684,7 @@ export function ProfilePage({
{/* Offers Button */}
<div className="mt-8 text-center">
<Button
onClick={()=>navigate("/super-savings")}
onClick={() => navigate("/super-savings")}
className="bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 text-white font-poppins px-8 py-3 font-normal"
>
<Star className="w-4 h-4 mr-2" />
@@ -725,17 +728,17 @@ export function ProfilePage({
<div className="space-y-2 text-sm font-poppins font-light">
<div className="flex justify-between">
{card.cardMode === "flexi" ? (
<>
<span>Attractions:</span>
<span>{card.noOfAttractions}</span>
</>
) : (
<>
<span>Days:</span>
<span>{card.noOfDays}</span>
</>
)
}
<>
<span>Attractions:</span>
<span>{card.noOfAttractions}</span>
</>
) : (
<>
<span>Days:</span>
<span>{card.noOfDays}</span>
</>
)
}
</div>
<div className="flex justify-between">
<span>Expired on:</span>
@@ -762,47 +765,47 @@ export function ProfilePage({
<h2 className="font-poppins text-2xl font-normal">My Itineraries</h2>
<Button
className="bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 text-white font-poppins font-normal"
onClick={()=>navigate("/create-itinerary-design")}
onClick={() => navigate("/create-itinerary-design")}
>
<Plus className="w-4 h-4 mr-2" />
Create Itinerary
</Button>
</div>
{mockItineraries.length > 0 ? (
{itineraries?.length > 0 ? (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{mockItineraries.map((itinerary) => (
{itineraries.map((itinerary: any) => (
<Card key={itinerary.id}>
<CardContent className="p-6">
<div className="flex items-start justify-between mb-4">
<div>
<h3 className="font-normal font-poppins">{itinerary.name}</h3>
<p className="text-sm text-gray-600 font-poppins font-light">{itinerary.city}</p>
<h3 className="font-normal font-poppins">{ }</h3>
<p className="text-sm text-gray-600 font-poppins font-light">{itinerary.city.cityName} Unlimited Card</p>
</div>
<Badge variant={itinerary.status === 'active' ? 'default' : 'secondary'}>
{itinerary.status}
<Badge variant={itinerary.isActive === true ? 'default' : 'secondary'}>
{itinerary.isActive ? "Active" : "Inactive"}
</Badge>
</div>
<div className="space-y-2 text-sm font-poppins font-light">
<div className="flex items-center gap-2">
<Calendar className="w-4 h-4 text-gray-500" />
<span>{itinerary.duration}</span>
<span>{itinerary.totalDays}</span>
</div>
<div className="flex items-center gap-2">
<MapPin className="w-4 h-4 text-gray-500" />
<span>{itinerary.attractions} attractions</span>
<span>{itinerary?.attractions} attractions</span>
</div>
<div className="flex items-center gap-2">
<Clock className="w-4 h-4 text-gray-500" />
<span>Created {new Date(itinerary.createdDate).toLocaleDateString()}</span>
<span>Created {new Date(itinerary.createdAt).toLocaleDateString()}</span>
</div>
</div>
<Button
variant="outline"
className="w-full mt-4 font-poppins font-normal"
onClick={onViewItineraryClick}
onClick={()=>navigate(`/itinerary-view-design/${itinerary.id}`)}
>
View Itinerary
</Button>