Files
CityCards-Website/src/components/LandingBookAttractionSection.tsx

339 lines
14 KiB
TypeScript

import { useState } from 'react';
import { ChevronLeft, ChevronRight, Clock, Users, Star, Zap, CheckCircle, MapPin, Volume2, Camera } from 'lucide-react';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { motion } from 'motion/react';
import { Button } from './ui/button';
const attractions = [
{
id: 1,
name: "Sydney Opera House",
city: "Sydney",
country: "Australia",
image: "https://images.unsplash.com/photo-1657622884558-cc7525f93638?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzeWRuZXklMjBvcGVyYSUyMGhvdXNlJTIwaGFyYm9yJTIwYnJpZGdlfGVufDF8fHx8MTc1NjExNDMwMHww&ixlib=rb-4.1.0&q=80&w=1080",
rating: 4.8,
reviews: "12,500+",
category: "Landmarks",
originalPrice: "$89",
includedValue: "$89",
perks: [
{ icon: Zap, label: "Skip-the-line", color: "text-green-600" },
{ icon: Volume2, label: "Audio guide", color: "text-blue-600" },
{ icon: Camera, label: "Photo spots", color: "text-purple-600" }
]
},
{
id: 2,
name: "Great Ocean Road",
city: "Melbourne",
country: "Australia",
image: "https://images.unsplash.com/photo-1557544780-585e99807b15?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxncmVhdCUyMG9jZWFuJTIwcm9hZCUyMHR3ZWx2ZSUyMGFwb3N0bGVzfGVufDF8fHx8MTc1NjExNDMwNHww&ixlib=rb-4.1.0&q=80&w=1080",
rating: 4.9,
reviews: "8,200+",
category: "Nature",
originalPrice: "$125",
includedValue: "$125",
perks: [
{ icon: Users, label: "Guided tour", color: "text-blue-600" },
{ icon: MapPin, label: "Transport", color: "text-green-600" },
{ icon: Camera, label: "Photo stops", color: "text-purple-600" }
]
},
{
id: 3,
name: "Lone Pine Koala Sanctuary",
city: "Brisbane",
country: "Australia",
image: "https://images.unsplash.com/photo-1625476038303-0d3022077d39?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxsb25lJTIwcGluZSUyMGtvYWxhJTIwc2FuY3R1YXJ5JTIwYnJpc2JhbmV8ZW58MXx8fHwxNzU2MTE0MzA3fDA&ixlib=rb-4.1.0&q=80&w=1080",
rating: 4.7,
reviews: "15,800+",
category: "Wildlife",
originalPrice: "$65",
includedValue: "$65",
perks: [
{ icon: Zap, label: "Skip-the-line", color: "text-green-600" },
{ icon: Users, label: "Animal encounters", color: "text-orange-600" },
{ icon: Camera, label: "Photo opportunities", color: "text-purple-600" }
]
},
{
id: 4,
name: "Kings Park",
city: "Perth",
country: "Australia",
image: "https://images.unsplash.com/photo-1667315682754-852d9e855207?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxraW5ncyUyMHBhcmslMjBwZXJ0aCUyMGJvdGFuaWNhbCUyMGdhcmRlbnxlbnwxfHx8fDE3NTYxMTQzMTJ8MA&ixlib=rb-4.1.0&q=80&w=1080",
rating: 4.6,
reviews: "9,400+",
category: "Parks",
originalPrice: "Free",
includedValue: "$35",
perks: [
{ icon: Users, label: "Walking tours", color: "text-blue-600" },
{ icon: Volume2, label: "Audio guide", color: "text-blue-600" },
{ icon: MapPin, label: "Trail maps", color: "text-green-600" }
]
},
{
id: 5,
name: "Barossa Valley",
city: "Adelaide",
country: "Australia",
image: "https://images.unsplash.com/photo-1578274821879-08e7f9050d83?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxiYXJvc3NhJTIwdmFsbGV5JTIwdmluZXlhcmQlMjB3aW5lcnl8ZW58MXx8fHwxNzU2MTE0MzE3fDA&ixlib=rb-4.1.0&q=80&w=1080",
rating: 4.8,
reviews: "6,700+",
category: "Wine Tours",
originalPrice: "$98",
includedValue: "$98",
perks: [
{ icon: Users, label: "Wine tastings", color: "text-purple-600" },
{ icon: MapPin, label: "Transport", color: "text-green-600" },
{ icon: Volume2, label: "Expert guide", color: "text-blue-600" }
]
}
];
const categories = ["All", "Landmarks", "Nature", "Wildlife", "Parks", "Wine Tours"];
export function LandingBookAttractionSection() {
const [activeCategory, setActiveCategory] = useState("All");
const filteredAttractions = activeCategory === "All"
? attractions
: attractions.filter(attraction => attraction.category === activeCategory);
const AttractionCard = ({ attraction, index }: { attraction: typeof attractions[0], index: number }) => (
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
viewport={{ once: true }}
className="group cursor-pointer flex-shrink-0 w-[280px] md:w-auto md:flex-shrink h-96 flip-card-container"
>
{/* 3D Flip Container */}
<div className="flip-card-inner group-hover:[transform:rotateY(180deg)] relative w-full h-full">
{/* FRONT FACE */}
<div className="flip-card-face absolute inset-0 w-full h-full rounded-2xl overflow-hidden shadow-lg">
{/* Background Image */}
<ImageWithFallback
src={attraction.image}
alt={attraction.name}
className="w-full h-full object-cover"
/>
{/* Rating Badge */}
{/* <div className="absolute top-4 right-4 bg-white/95 backdrop-blur-sm rounded-full px-3 py-1.5 flex items-center gap-1 shadow-lg z-10">
<div className="w-4 h-4 bg-yellow-500 rounded-full flex items-center justify-center">
<span className="text-white text-xs">★</span>
</div>
<span className="font-poppins text-sm font-medium text-foreground">{attraction.rating}</span>
</div> */}
{/* Front Content - Clean Title & Location */}
<div className="absolute bottom-0 left-0 right-0">
<div className="bg-black/50 p-6">
<h3 className="font-poppins text-lg md:text-xl leading-snug font-semibold text-white mb-1">{attraction.name}</h3>
<p className="font-poppins text-sm leading-relaxed font-normal text-white/90">
{attraction.city}, {attraction.country}
</p>
</div>
</div>
</div>
{/* BACK FACE */}
<div className="flip-card-face flip-card-back absolute inset-0 w-full h-full rounded-2xl overflow-hidden shadow-lg bg-gray-900">
{/* Back Content Container */}
<div className="relative w-full h-full p-6 flex flex-col justify-center text-white">
{/* Included Value Section */}
<div className="mb-4">
<div className="inline-flex items-center gap-2 bg-warm-coral text-white px-3 py-1.5 rounded-full text-sm font-medium mb-3">
<CheckCircle className="w-4 h-4" />
<span>Included Value</span>
</div>
<div className="text-2xl font-bold mb-1">{attraction.includedValue}</div>
<p className="text-white/80 text-sm">
{attraction.originalPrice === "Free"
? "Premium access included"
: "Save money with CityCard"}
</p>
</div>
{/* What's Included List */}
<div className="mb-4">
<h4 className="font-semibold text-sm mb-3">What's Included:</h4>
<div className="space-y-2">
{attraction.perks.slice(0, 3).map((perk, perkIndex) => (
<div key={perkIndex} className="flex items-center gap-3 text-white/90">
<div className="w-6 h-6 rounded-full bg-white/20 backdrop-blur-sm flex items-center justify-center">
<perk.icon className="w-3 h-3 text-white" />
</div>
<span className="text-sm">{perk.label}</span>
</div>
))}
</div>
</div>
{/* Duration & Meta Info */}
<div className="mb-4">
<div className="flex items-center gap-4 text-white/80 text-sm">
<div className="flex items-center gap-1">
<Clock className="w-4 h-4" />
<span>2-3 hours</span>
</div>
<div className="flex items-center gap-1">
<Users className="w-4 h-4" />
<span>All ages</span>
</div>
</div>
</div>
{/* Footer Features */}
<div className="border-t border-white/20 pt-4">
<div className="flex items-center justify-between text-white/80 text-xs">
<div className="flex items-center gap-2">
<MapPin className="w-3 h-3" />
<span>Mobile ticket</span>
</div>
<div className="flex items-center gap-2">
<CheckCircle className="w-3 h-3" />
<span>Instant confirmation</span>
</div>
</div>
</div>
{/* Decorative Elements */}
<div className="absolute top-4 right-4 w-16 h-16 bg-primary/20 rounded-full blur-xl"></div>
<div className="absolute bottom-4 left-4 w-12 h-12 bg-primary/15 rounded-full blur-lg"></div>
</div>
</div>
</div>
</motion.div>
);
return (
<section className="py-20 bg-gray-50 relative overflow-hidden">
<div className="container mx-auto px-4">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="text-center mb-16"
>
<div className="inline-flex items-center gap-2 bg-primary/10 px-4 py-2 rounded-full mb-6">
<div className="w-2 h-2 bg-primary rounded-full"></div>
<span className="font-poppins text-sm font-medium text-primary">
Must-See Destinations
</span>
</div>
<h2 className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-tight text-foreground mb-4">
<span className="font-bold text-primary italic">
Top
</span>{' '}
<span className="font-normal">Attractions</span>
</h2>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-600 max-w-3xl mx-auto">
Discover Australia's most iconic attractions and hidden gems across Sydney, Melbourne, Brisbane, Perth, and Adelaide - all included with your CityCard
</p>
</motion.div>
{/* Category Tabs */}
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
viewport={{ once: true }}
className="flex flex-wrap justify-center gap-3 mb-12"
>
{categories.map((category, index) => (
<motion.button
key={category}
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
viewport={{ once: true }}
onClick={() => setActiveCategory(category)}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className={`px-6 py-4 h-14 rounded-full font-medium transition-all duration-300 ${activeCategory === category
? 'bg-red-400 text-white shadow-xl shadow-warm-coral/25 ring-2 ring-warm-coral/20'
: 'bg-white/80 backdrop-blur-sm text-gray-700 hover:text-gray-900 hover:shadow-lg border border-gray-200/50 hover:border-warm-coral/20 hover:bg-white'
}`}
>
{category}
</motion.button>
))}
</motion.div>
{/* Mobile Horizontal Carousel */}
<div className="block md:hidden mb-8">
<div className="relative">
{/* Scroll Container */}
<div className="flex gap-6 overflow-x-auto scrollbar-hide pb-4 px-4 -mx-4">
{filteredAttractions.map((attraction, index) => (
<AttractionCard key={attraction.id} attraction={attraction} index={index} />
))}
</div>
{/* Scroll Indicators */}
<div className="flex justify-center mt-6 gap-2">
{Array.from({ length: Math.ceil(filteredAttractions.length / 2) }).map((_, index) => (
<div
key={index}
className="w-2 h-2 rounded-full bg-gray-300"
/>
))}
</div>
{/* Mobile Hint Text */}
<div className="text-center mt-4">
<p className="text-sm text-gray-500">
Swipe to explore more attractions
</p>
</div>
</div>
</div>
{/* Desktop Bento Grid */}
<div className="hidden md:block w-full">
{/* Top Row - 3 equal cards */}
<div className="grid grid-cols-3 gap-6">
{filteredAttractions.slice(0, 3).map((attraction, index) => (
<AttractionCard key={attraction.id} attraction={attraction} index={index} />
))}
</div>
{/* Consistent Vertical Spacing */}
<div className="h-6"></div>
{/* Bottom Row - 2 larger cards */}
<div className="grid grid-cols-2 gap-6">
{filteredAttractions.slice(3, 5).map((attraction, index) => (
<AttractionCard key={attraction.id} attraction={attraction} index={index + 3} />
))}
</div>
</div>
{/* Call to Action */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
viewport={{ once: true }}
className="text-center mt-12"
>
<Button
withShine={true}
size="xl"
className="bg-primary hover:bg-primary/90 py-4 rounded-full text-lg font-poppins font-semibold px-8 text-white shadow-lg hover:shadow-xl transition-all duration-300"
>
Get Your City Card
</Button>
</motion.div>
</div>
</section>
);
}