main #12
@@ -185,7 +185,7 @@ function App() {
|
||||
</div>
|
||||
|
||||
{/* Card Title in Orange */}
|
||||
<p className="absolute font-poppins font-medium leading-[1.3] left-[50%] text-[#ffb23f] text-[24px] text-center top-[65px] tracking-[-0.96px] translate-x-[-50%] w-[202px]" style={{ fontVariationSettings: "'wdth' 100" }}>
|
||||
<p className="absolute font-appoppins font-medium leading-[1.3] left-[50%] text-[#ffb23f] text-[24px] text-center top-[65px] tracking-[-0.96px] translate-x-[-50%] w-[202px]" style={{ fontVariationSettings: "'wdth' 100" }}>
|
||||
{stickyCardType === 'unlimited' ? (
|
||||
<>Melbourne Unlimited Card</>
|
||||
) : (
|
||||
|
||||
@@ -37,6 +37,8 @@ import { PaymentDetailsPage } from './pages/PaymentDetailsPage';
|
||||
import { CartPageDesign } from './pages/CartPageDesign';
|
||||
import { CheckoutPage2 } from './pages/CheckoutPage2';
|
||||
import { SuperSavingsDetailsPage } from './pages/SuperSavingsDetailsPage';
|
||||
import { ViewCardDetailsPage } from './pages/ViewCardDetailsPageDesign';
|
||||
import { CreateMagicItineraryPageDesign } from './pages/CreateMagicIternaryPageDesign';
|
||||
|
||||
// User type definition
|
||||
interface User {
|
||||
@@ -194,6 +196,12 @@ export function AppRouter({
|
||||
<ProfilePage {...commonNavHandlers} />
|
||||
</motion.div>
|
||||
} />
|
||||
<Route path="/view-card-design" element={
|
||||
<motion.div key="profile" {...pageTransition}>
|
||||
<ViewCardDetailsPage {...commonNavHandlers} />
|
||||
</motion.div>
|
||||
} />
|
||||
|
||||
|
||||
{/* Itinerary Routes */}
|
||||
<Route path="/create-itinerary" element={
|
||||
@@ -201,12 +209,22 @@ export function AppRouter({
|
||||
<CreateMagicItineraryPage {...commonNavHandlers} />
|
||||
</motion.div>
|
||||
} />
|
||||
<Route path="/create-itinerary-design" element={
|
||||
<motion.div key="create-itinerary" {...pageTransition}>
|
||||
<CreateMagicItineraryPageDesign {...commonNavHandlers} />
|
||||
</motion.div>
|
||||
} />
|
||||
|
||||
<Route path="/itinerary-view" element={
|
||||
<motion.div key="itinerary-view" {...pageTransition}>
|
||||
<ItineraryViewPage {...commonNavHandlers} />
|
||||
</motion.div>
|
||||
} />
|
||||
<Route path="/itinerary-view-design" element={
|
||||
<motion.div key="itinerary-view" {...pageTransition}>
|
||||
<ItineraryViewPage {...commonNavHandlers} />
|
||||
</motion.div>
|
||||
} />
|
||||
|
||||
<Route path="/magic-itinerary" element={
|
||||
<motion.div key="magic-itinerary" {...pageTransition}>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { citiesApi } from "./services/cities.service";
|
||||
import { authApi } from "./services/auth.service";
|
||||
import { profileApi } from "./services/profile.service";
|
||||
import { cardsApi } from "./services/cards.service";
|
||||
import { itineraryApi } from "./services/itinerary.service";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
@@ -11,7 +12,8 @@ export const store = configureStore({
|
||||
[citiesApi.reducerPath]: citiesApi.reducer,
|
||||
[authApi.reducerPath]: authApi.reducer,
|
||||
[profileApi.reducerPath]: profileApi.reducer,
|
||||
[cardsApi.reducerPath]:cardsApi.reducer
|
||||
[cardsApi.reducerPath]:cardsApi.reducer,
|
||||
[itineraryApi.reducerPath]:itineraryApi.reducer
|
||||
|
||||
},
|
||||
|
||||
@@ -21,7 +23,8 @@ export const store = configureStore({
|
||||
citiesApi.middleware,
|
||||
authApi.middleware,
|
||||
profileApi.middleware,
|
||||
cardsApi.middleware
|
||||
cardsApi.middleware,
|
||||
itineraryApi.middleware
|
||||
),
|
||||
});
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
|
||||
28
src/Redux/services/itinerary.service.ts
Normal file
28
src/Redux/services/itinerary.service.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
import { createApi} from "@reduxjs/toolkit/query/react";
|
||||
import { baseQuery } from "../baseQuery";
|
||||
|
||||
export const itineraryApi = createApi({
|
||||
reducerPath: "itApi",
|
||||
baseQuery,
|
||||
|
||||
endpoints: (builder) => ({
|
||||
|
||||
|
||||
|
||||
createMagicItinerary: builder.mutation({
|
||||
query: (itineraryDetails) => ({ // keep the name of the variables being passed here same as when calling the mutation hook
|
||||
url: `/website/itinerary`,
|
||||
method: "POST",
|
||||
body: itineraryDetails
|
||||
}),
|
||||
|
||||
}),
|
||||
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
export const {
|
||||
useCreateMagicItineraryMutation,
|
||||
} = itineraryApi;
|
||||
@@ -425,9 +425,9 @@ export default function Navbar({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeCity.toLowerCase() === 'melbourne') {
|
||||
if (activeCity?.toLowerCase() === 'melbourne') {
|
||||
setLastKnownCity('melbourne');
|
||||
} else if (activeCity.toLowerCase() === 'landing' || activeCity.toLowerCase() === 'landingpage') {
|
||||
} else if (activeCity?.toLowerCase() === 'landing' || activeCity?.toLowerCase() === 'landingpage') {
|
||||
setLastKnownCity('landing');
|
||||
}
|
||||
}, [activeCity]);
|
||||
@@ -692,7 +692,7 @@ export default function Navbar({
|
||||
label: 'My Profile',
|
||||
icon: <User className="w-4 h-4" />,
|
||||
action: () => {
|
||||
navigate(citySelected ? `/${slugify(cityName)}/profile` : '/profile');
|
||||
navigate(citySelected ? `/profile` : '/profile');
|
||||
setActiveUserDropdown(false);
|
||||
}
|
||||
},
|
||||
|
||||
1125
src/pages/CreateMagicIternaryPageDesign.tsx
Normal file
1125
src/pages/CreateMagicIternaryPageDesign.tsx
Normal file
File diff suppressed because it is too large
Load Diff
746
src/pages/ItineraryViewPageDesign.tsx
Normal file
746
src/pages/ItineraryViewPageDesign.tsx
Normal file
@@ -0,0 +1,746 @@
|
||||
import React, { useState } from 'react';
|
||||
import { motion } from 'motion/react';
|
||||
import { ArrowLeft, Calendar, Clock, MapPin, Users, Star, Heart, Share2, Download, CheckCircle, Navigation, Cloud, Sun } from 'lucide-react';
|
||||
import { Button } from '../components/ui/button';
|
||||
import { Card, CardContent } from '../components/ui/card';
|
||||
import { Badge } from '../components/ui/badge';
|
||||
import Navbar from '../components/Navbar';
|
||||
import { Footer } from '../components/Footer';
|
||||
import { ImageWithFallback } from '../components/figma/ImageWithFallback';
|
||||
|
||||
interface ItineraryViewPageDesignProps {
|
||||
onBackClick: () => void;
|
||||
onHomeClick: () => void;
|
||||
onMelbourneClick: () => void;
|
||||
onPassesClick: () => void;
|
||||
onCheckoutClick: () => void;
|
||||
onSignInClick: () => void;
|
||||
onSignOutClick: () => void;
|
||||
onAttractionsClick: () => void;
|
||||
onBlogsClick: () => void;
|
||||
onHowItWorksClick: () => void;
|
||||
onFAQClick: () => void;
|
||||
onPrivacyPolicyClick: () => void;
|
||||
onAboutUsClick: () => void;
|
||||
onProfileClick: () => void;
|
||||
onCityCardsClick: () => void;
|
||||
onMagicItineraryClick: () => void;
|
||||
onPostCardsClick: () => void;
|
||||
onOffersClick: () => void;
|
||||
onCreateItineraryClick: () => void;
|
||||
onContactUsClick?: () => void;
|
||||
onEsimsClick?: () => void;
|
||||
onHotelDiscountsClick?: () => void;
|
||||
currentPage: string;
|
||||
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,
|
||||
onMelbourneClick,
|
||||
onPassesClick,
|
||||
onCheckoutClick,
|
||||
onSignInClick,
|
||||
onSignOutClick,
|
||||
onAttractionsClick,
|
||||
onBlogsClick,
|
||||
onHowItWorksClick,
|
||||
onFAQClick,
|
||||
onPrivacyPolicyClick,
|
||||
onAboutUsClick,
|
||||
onProfileClick,
|
||||
onCityCardsClick,
|
||||
onMagicItineraryClick,
|
||||
onPostCardsClick,
|
||||
onOffersClick,
|
||||
onCreateItineraryClick,
|
||||
onContactUsClick,
|
||||
onEsimsClick,
|
||||
onHotelDiscountsClick,
|
||||
currentPage,
|
||||
user
|
||||
}: ItineraryViewPageDesignProps) {
|
||||
const [viewMode, setViewMode] = useState<'daily' | 'summary'>('daily');
|
||||
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;
|
||||
});
|
||||
};
|
||||
|
||||
// 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'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
{/* Navbar */}
|
||||
<Navbar
|
||||
activeCity=""
|
||||
onCityChange={() => {}}
|
||||
onSignInClick={onSignInClick}
|
||||
onPassesClick={onPassesClick}
|
||||
onCheckoutClick={onCheckoutClick}
|
||||
onHomeClick={onHomeClick}
|
||||
onMelbourneClick={onMelbourneClick}
|
||||
onAttractionsClick={onAttractionsClick}
|
||||
onBlogsClick={onBlogsClick}
|
||||
onHowItWorksClick={onHowItWorksClick}
|
||||
onFAQClick={onFAQClick}
|
||||
onPrivacyPolicyClick={onPrivacyPolicyClick}
|
||||
onAboutUsClick={onAboutUsClick}
|
||||
onProfileClick={onProfileClick}
|
||||
onCityCardsClick={onCityCardsClick}
|
||||
onMagicItineraryClick={onMagicItineraryClick}
|
||||
onPostCardsClick={onPostCardsClick}
|
||||
onOffersClick={onOffersClick}
|
||||
currentPage="itinerary-view"
|
||||
isUserSignedIn={!!user}
|
||||
user={user}
|
||||
/>
|
||||
|
||||
{/* Header Section */}
|
||||
<section className="pt-32 pb-8 bg-gradient-to-br from-primary/5 to-secondary/5">
|
||||
<div className="container mx-auto px-4 pt-32">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="text-center"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={onBackClick}
|
||||
className="mb-6 hover:bg-primary/5 font-poppins font-medium"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
Back to Magic Itinerary
|
||||
</Button>
|
||||
|
||||
<div className="flex items-center justify-center gap-2 text-primary mb-4">
|
||||
<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>
|
||||
</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}!
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Main Content */}
|
||||
<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">
|
||||
<Button
|
||||
variant={viewMode === 'daily' ? 'default' : 'ghost'}
|
||||
size="sm"
|
||||
onClick={() => setViewMode('daily')}
|
||||
className="rounded-md font-poppins font-medium"
|
||||
>
|
||||
Daily View
|
||||
</Button>
|
||||
<Button
|
||||
variant={viewMode === 'summary' ? 'default' : 'ghost'}
|
||||
size="sm"
|
||||
onClick={() => setViewMode('summary')}
|
||||
className="rounded-md font-poppins font-medium"
|
||||
>
|
||||
Summary
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Itinerary Overview */}
|
||||
<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>
|
||||
</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>
|
||||
<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>
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
|
||||
{/* Daily Plans - Enhanced View */}
|
||||
{viewMode === 'daily' && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
className="space-y-12"
|
||||
>
|
||||
{generatedItinerary.dailyPlans.map((day, dayIndex) => (
|
||||
<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;
|
||||
})() && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4 + dayIndex * 0.1 }}
|
||||
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}
|
||||
</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>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Day Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.5 + dayIndex * 0.1 }}
|
||||
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>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-merchant text-2xl md:text-3xl leading-snug font-semibold">Day {day.day}</h3>
|
||||
<p className="font-poppins text-base text-muted-foreground font-normal">{day.title}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* GMT Label */}
|
||||
<div className="pl-2">
|
||||
<p className="font-poppins text-sm text-gray-500 font-normal">GMT</p>
|
||||
</div>
|
||||
|
||||
{/* Activity Cards - Desktop Grid Layout */}
|
||||
<div className="space-y-8">
|
||||
{day.activities.map((activity, actIndex) => {
|
||||
const activityKey = `day${day.day}-act${actIndex}`;
|
||||
const isFavorite = favorites.has(activityKey);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={actIndex}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.6 + dayIndex * 0.1 + actIndex * 0.05 }}
|
||||
className="flex gap-6"
|
||||
>
|
||||
{/* 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>
|
||||
|
||||
{/* 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 */}
|
||||
<div className="relative h-64 md:h-72 bg-gray-200">
|
||||
<ImageWithFallback
|
||||
src={activity.image}
|
||||
alt={activity.activity}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
|
||||
{/* Favorite Heart Button - Top Right */}
|
||||
<div className="absolute top-4 right-4">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="secondary"
|
||||
className="bg-white/95 hover:bg-white shadow-lg backdrop-blur-sm rounded-full w-12 h-12"
|
||||
onClick={() => toggleFavorite(activityKey)}
|
||||
>
|
||||
<Heart className={`w-5 h-5 ${isFavorite ? 'fill-primary text-primary' : 'text-gray-700'}`} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Get Directions Button - Bottom Left */}
|
||||
<div className="absolute bottom-4 left-4">
|
||||
<Button
|
||||
className="bg-primary hover:bg-primary/90 text-white font-poppins font-semibold shadow-lg px-6 py-3 rounded-xl"
|
||||
>
|
||||
<Navigation className="w-4 h-4 mr-2" />
|
||||
Get Directions
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content Section */}
|
||||
<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}
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Category Badges */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{activity.categories.map((category, catIndex) => (
|
||||
<Badge
|
||||
key={catIndex}
|
||||
variant="secondary"
|
||||
className="font-poppins font-normal text-sm bg-primary/10 text-primary hover:bg-primary/20 px-3 py-1"
|
||||
>
|
||||
{category}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Activity Details - Bullet Points */}
|
||||
<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>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Summary View */}
|
||||
{viewMode === 'summary' && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Action Buttons */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.5 }}
|
||||
className="flex flex-col sm:flex-row gap-4 justify-center pt-8"
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onCreateItineraryClick}
|
||||
className="font-poppins font-medium px-8 py-3 text-lg"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<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"
|
||||
>
|
||||
<Share2 className="w-5 h-5 mr-2" />
|
||||
Share Trip
|
||||
</Button>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<Footer
|
||||
onHomeClick={onHomeClick}
|
||||
onMelbourneClick={onMelbourneClick}
|
||||
onPassesClick={onPassesClick}
|
||||
onSignInClick={onSignInClick}
|
||||
onAttractionsClick={onAttractionsClick}
|
||||
onBlogsClick={onBlogsClick}
|
||||
onHowItWorksClick={onHowItWorksClick}
|
||||
onFAQClick={onFAQClick}
|
||||
onPrivacyPolicyClick={onPrivacyPolicyClick}
|
||||
onContactUsClick={onContactUsClick}
|
||||
currentPage={currentPage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user