294 lines
11 KiB
TypeScript
294 lines
11 KiB
TypeScript
import { useState } from 'react';
|
|
import { Check, X, Star, Users, MapPin, Calendar, Clock, Zap, Eye } from 'lucide-react';
|
|
import { Button } from './ui/button';
|
|
import { motion } from 'motion/react';
|
|
|
|
// const cardOptions = [
|
|
// {
|
|
// id: 'selective',
|
|
// name: 'Flexi Card',
|
|
// subtitle: 'Pick 5-10 things to do from a choice of 102 attractions tours and activities',
|
|
// priceRange: '$89-159',
|
|
// duration: '3-7 days',
|
|
// popular: false,
|
|
// color: 'from-blue-500 to-cyan-500',
|
|
// features: {
|
|
// passCategory: 'Selective Card',
|
|
// accessToAttractions: true,
|
|
// entryToAttractions: true,
|
|
// accessToExperiences: true,
|
|
// entryToSites: true,
|
|
// accessToVenues: false,
|
|
// entryToEvents: 'Pass Category',
|
|
// accessToLocations: 'Pass Category',
|
|
// entryToActivities: true,
|
|
// accessToExhibits: true,
|
|
// entryToActivitiesSecond: true
|
|
// }
|
|
// },
|
|
// {
|
|
// id: 'unlimited',
|
|
// name: 'Melbourne Unlimited Card',
|
|
// subtitle: 'Pick 5-30 things to do from a choice of 102 attractions tours and activities',
|
|
// priceRange: '$159-299',
|
|
// duration: '3-7 days',
|
|
// popular: true,
|
|
// color: 'from-purple-500 to-pink-500',
|
|
// features: {
|
|
// passCategory: 'Pass Category',
|
|
// accessToAttractions: true,
|
|
// entryToAttractions: true,
|
|
// accessToExperiences: true,
|
|
// entryToSites: true,
|
|
// accessToVenues: true,
|
|
// entryToEvents: 'Pass Category',
|
|
// accessToLocations: 'Pass Category',
|
|
// entryToActivities: true,
|
|
// accessToExhibits: true,
|
|
// entryToActivitiesSecond: true
|
|
// }
|
|
// }
|
|
// ];
|
|
|
|
const features = [
|
|
{ key: 'passCategory', label: 'Pass Category', icon: Star },
|
|
{ key: 'accessToAttractions', label: 'Access to Attractions', icon: MapPin },
|
|
{ key: 'entryToAttractions', label: 'Entry to Attractions', icon: Zap },
|
|
{ key: 'accessToExperiences', label: 'Access to Experiences', icon: Users },
|
|
{ key: 'entryToSites', label: 'Entry to Sites', icon: MapPin },
|
|
{ key: 'accessToVenues', label: 'Access to Venues', icon: MapPin },
|
|
{ key: 'entryToEvents', label: 'Entry to Events', icon: Calendar },
|
|
{ key: 'accessToLocations', label: 'Access to Locations', icon: MapPin },
|
|
{ key: 'entryToActivities', label: 'Entry to Activities', icon: Users },
|
|
{ key: 'accessToExhibits', label: 'Access to Exhibits', icon: Eye },
|
|
{ key: 'entryToActivitiesSecond', label: 'Entry to Activities', icon: Users }
|
|
];
|
|
|
|
const FeatureIcon = ({ feature }: { feature: typeof features[0] }) => {
|
|
const Icon = feature.icon;
|
|
return <Icon className="w-4 h-4 text-gray-500" />;
|
|
};
|
|
|
|
interface MelbourneCardComparisonProps {
|
|
onCheckoutClick?: () => void;
|
|
cards: any[]
|
|
}
|
|
|
|
export function MelbourneCardComparison({ onCheckoutClick,cards }: MelbourneCardComparisonProps) {
|
|
const [selectedCard, setSelectedCard] = useState<string>('unlimited');
|
|
|
|
const cardOptions = [
|
|
{
|
|
id: cards[0]?.id,
|
|
name: cards[0]?.title,
|
|
subtitle: cards[0]?.description,
|
|
priceRange: `$${cards[0]?.adultPrice}`,
|
|
duration: '3-7 days',
|
|
popular: false,
|
|
color: 'from-blue-500 to-cyan-500',
|
|
features: {
|
|
passCategory: 'Selective Card',
|
|
accessToAttractions: true,
|
|
entryToAttractions: true,
|
|
accessToExperiences: true,
|
|
entryToSites: true,
|
|
accessToVenues: false,
|
|
entryToEvents: 'Pass Category',
|
|
accessToLocations: 'Pass Category',
|
|
entryToActivities: true,
|
|
accessToExhibits: true,
|
|
entryToActivitiesSecond: true
|
|
}
|
|
},
|
|
{
|
|
id: cards[1]?.id,
|
|
name: cards[1]?.title,
|
|
subtitle: cards[1]?.description,
|
|
priceRange: `$${cards[1]?.adultPrice}`,
|
|
duration: '3-7 days',
|
|
popular: true,
|
|
color: 'from-purple-500 to-pink-500',
|
|
features: {
|
|
passCategory: 'Pass Category',
|
|
accessToAttractions: true,
|
|
entryToAttractions: true,
|
|
accessToExperiences: true,
|
|
entryToSites: true,
|
|
accessToVenues: true,
|
|
entryToEvents: 'Pass Category',
|
|
accessToLocations: 'Pass Category',
|
|
entryToActivities: true,
|
|
accessToExhibits: true,
|
|
entryToActivitiesSecond: true
|
|
}
|
|
}
|
|
];
|
|
|
|
const renderFeatureValue = (value: boolean | string, cardId: string) => {
|
|
if (typeof value === 'boolean') {
|
|
return value ? (
|
|
<div className="flex justify-center">
|
|
<div className="w-6 h-6 bg-green-500 rounded-full flex items-center justify-center">
|
|
<Check className="w-4 h-4 text-white" />
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="flex justify-center">
|
|
<div className="w-6 h-6 bg-gray-200 rounded-full flex items-center justify-center">
|
|
<X className="w-3 h-3 text-gray-400" />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="text-center text-sm text-gray-600 px-2">
|
|
{value}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<section className="py-20 bg-gradient-to-br from-gray-50 via-white to-gray-50 relative overflow-hidden">
|
|
{/* Background Pattern */}
|
|
<div className="absolute inset-0 opacity-5">
|
|
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-br from-primary/10 to-secondary/10"></div>
|
|
</div>
|
|
|
|
<div className="container mx-auto px-4 relative z-10">
|
|
{/* 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-gradient-to-r from-primary/10 to-secondary/10 px-4 py-2 rounded-full mb-6">
|
|
<div className="w-2 h-2 bg-gradient-to-r from-primary to-secondary rounded-full"></div>
|
|
<span className="text-sm font-medium bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
|
|
Choose Your Adventure
|
|
</span>
|
|
</div>
|
|
|
|
<h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6">
|
|
<span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic pr-2">
|
|
Buy
|
|
</span>{' '}
|
|
<span className="font-normal">Now</span>
|
|
</h2>
|
|
|
|
<p className="text-xl text-gray-600 max-w-4xl mx-auto leading-relaxed">
|
|
Melbourne is a must-visit cultural epicenter, and this spectacular trip unlocks
|
|
your access around the city in one easy. Save over the cost of visiting Melbourne's
|
|
landmarks, have lunch at Phi Phi Leh, snorkel at Bamboo Island, and visit Monkey Beach.
|
|
</p>
|
|
</motion.div>
|
|
|
|
{/* Comparison Table */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.6, delay: 0.3 }}
|
|
viewport={{ once: true }}
|
|
className="bg-white rounded-3xl shadow-xl border border-gray-100 overflow-hidden"
|
|
>
|
|
{/* Table Header */}
|
|
<div className="bg-gradient-to-r from-gray-50 to-gray-100 px-8 py-6">
|
|
<div className="grid grid-cols-3 gap-8 items-center">
|
|
<div className="font-poppins text-xl font-semibold text-gray-900">
|
|
Features
|
|
</div>
|
|
{cardOptions.map((card) => (
|
|
<div key={card.id} className="text-center">
|
|
<div className="mb-2">
|
|
<div className="font-poppins font-semibold text-2xl" style={{ color: '#F95F62' }}>{card.name}</div>
|
|
</div>
|
|
<div className="font-poppins text-sm text-gray-600 max-w-xs mx-auto leading-relaxed">
|
|
{card.subtitle}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Table Body */}
|
|
<div className="divide-y divide-gray-100">
|
|
{features.map((feature, index) => (
|
|
<motion.div
|
|
key={feature.key}
|
|
initial={{ opacity: 0, x: -20 }}
|
|
whileInView={{ opacity: 1, x: 0 }}
|
|
transition={{ duration: 0.4, delay: index * 0.05 }}
|
|
viewport={{ once: true }}
|
|
className="grid grid-cols-3 gap-8 items-center px-8 py-6 hover:bg-gray-50/50 transition-colors duration-200"
|
|
>
|
|
<div className="flex items-center gap-3">
|
|
<FeatureIcon feature={feature} />
|
|
<span className="font-medium text-gray-900">{feature.label}</span>
|
|
</div>
|
|
|
|
{cardOptions.map((card) => (
|
|
<div key={card.id} className="text-center">
|
|
{renderFeatureValue(card.features[feature.key as keyof typeof card.features], card.id)}
|
|
</div>
|
|
))}
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Call to Action Footer */}
|
|
<div className="bg-gradient-to-r from-primary/5 to-secondary/5 px-8 py-8">
|
|
<div className="grid grid-cols-3 gap-8 items-center">
|
|
<div className="text-center">
|
|
<div className="font-medium text-gray-600 text-sm mb-2">Ready to explore?</div>
|
|
<div className="text-xs text-gray-500">Compare features above</div>
|
|
</div>
|
|
|
|
{cardOptions.map((card) => (
|
|
<motion.div key={card.id} className="text-center">
|
|
<div className="mb-4">
|
|
<div className="text-3xl font-bold text-gray-900">{card.priceRange}</div>
|
|
<div className="text-sm text-gray-600">{card.duration}</div>
|
|
</div>
|
|
<Button
|
|
withShine={true}
|
|
className="w-full h-14 rounded-2xl text-white font-semibold text-lg hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
|
|
style={{ backgroundColor: '#F95F62' }}
|
|
onClick={onCheckoutClick}
|
|
>
|
|
Buy {card.name}
|
|
</Button>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* Additional Info */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5, delay: 0.4 }}
|
|
viewport={{ once: true }}
|
|
className="text-center mt-12"
|
|
>
|
|
<div className="flex flex-wrap justify-center gap-8 text-sm text-gray-600">
|
|
<div className="flex items-center gap-2">
|
|
<Clock className="w-4 h-4 text-primary" />
|
|
<span>Instant digital delivery</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Users className="w-4 h-4 text-primary" />
|
|
<span>24/7 customer support</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Star className="w-4 h-4 text-primary" />
|
|
<span>90-day money back guarantee</span>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
);
|
|
} |