Files
CityCards-Website/src/pages/ViewCardDetailsPageDesign.tsx

431 lines
17 KiB
TypeScript

import { useState } from 'react';
import { motion } from 'motion/react';
import { ArrowLeft, ChevronRight, QrCode, CreditCard, Calendar, MapPin, Star, CheckCircle, Sparkles, Users, Clock, Gift, Ticket } 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';
import { useGetUserCardDetailsQuery } from '../Redux/services/profile.service';
import { useNavigate, useParams } from 'react-router-dom';
import LoadingSpinner from '../components/LoadingSpinner';
interface User {
email: string;
name: string;
}
interface ViewCardDetailsPageProps {
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;
onContactUsClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
currentPage: string;
user: User | null;
}
export function ViewCardDetailsPage({
onBackClick,
onHomeClick,
onMelbourneClick,
onPassesClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onProfileClick,
onCityCardsClick,
onMagicItineraryClick,
onPostCardsClick,
onOffersClick,
onContactUsClick,
onEsimsClick,
onHotelDiscountsClick,
currentPage,
user
}: ViewCardDetailsPageProps) {
// Card type state
const [cardType, setCardType] = useState<'unlimited' | 'flexi'>('unlimited');
const { cardId } = useParams()
const { data, isLoading } = useGetUserCardDetailsQuery(cardId)
const navigate = useNavigate()
const baseUrl = import.meta.env.VITE_BASE_URL;
const cardDetails = data?.bookingDetails ?? {}
const attractions = data?.attractions ?? []
const offers = data?.offers ?? []
if (isLoading) {
return (
<LoadingSpinner />
)
}
// Mock data for the current pass
const currentPass = {
name: 'Melbourne- Unlimited Card',
status: 'Active',
date: '22/12/2024',
duration: '2 Days',
adults: 3,
kids: 3
};
// Generate QR code pattern
const generateQRPattern = () => {
const size = 17;
const pattern = [];
for (let i = 0; i < size * size; i++) {
const row = Math.floor(i / size);
const col = i % size;
// Corner squares (5x5 for smaller QR)
const isCornerSquare =
(row < 5 && col < 5) || // Top-left
(row < 5 && col >= 12) || // Top-right
(row >= 12 && col < 5); // Bottom-left
// Finder patterns within corner squares
const isFinderPattern = isCornerSquare && (
(row === 0 || row === 4 || col === 0 || col === 4) ||
(row >= 2 && row <= 2 && col >= 2 && col <= 2)
);
// Timing patterns
const isTimingPattern = (row === 4 && col >= 6 && col <= 10) || (col === 4 && row >= 6 && row <= 10);
// Random data pattern for other areas
const isDataPattern = !isCornerSquare && !isTimingPattern && Math.random() > 0.4;
pattern.push(isFinderPattern || isTimingPattern || isDataPattern);
}
return pattern;
};
const qrPattern = generateQRPattern();
return (
<div className="min-h-screen bg-[#f8f8f8]">
<Navbar
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
currentPage={currentPage}
isUserSignedIn={!!user}
user={user}
/>
<div className="container mx-auto px-4 pt-40 pb-6">
{/* Back Button */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
className="mb-6"
>
<Button
variant="ghost"
onClick={() => navigate(-1)}
className="p-0 hover:bg-transparent"
>
<div className="text-gray-600 hover:text-primary transition-colors duration-200 flex items-center gap-2">
<ArrowLeft className="w-4 h-4" />
<span>Back</span>
</div>
</Button>
</motion.div>
{/* Pass Card - Redesigned */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<Card className="border-0 shadow-lg overflow-hidden">
{/* Gradient Header Bar */}
<div className="h-2 bg-gradient-to-r from-primary via-secondary to-primary"></div>
<CardContent className="p-6">
{/* Card Header */}
<div className="flex items-start justify-between mb-6">
<div className="flex items-start gap-4">
{/* Icon */}
<div className="w-14 h-14 rounded-xl bg-gradient-to-br from-primary/10 to-secondary/10 flex items-center justify-center flex-shrink-0">
<Ticket className="w-7 h-7 text-primary" />
</div>
{/* Title and Status */}
<div>
<h2 className="font-merchant text-xl font-semibold text-gray-900 mb-2">
{cardDetails.name}
</h2>
<Badge className="bg-gradient-to-r from-green-500 to-green-600 text-white px-3 py-1 rounded-full">
<div className="w-2 h-2 bg-white rounded-full mr-2 animate-pulse"></div>
{cardDetails.isActive ? "Active" : "Inactive"}
</Badge>
</div>
</div>
{/* Date Badge */}
<div className="bg-gradient-to-br from-primary/5 to-secondary/5 px-4 py-2 rounded-lg">
<div className="flex items-center gap-2 text-primary">
<Calendar className="w-4 h-4" />
<span className="font-poppins font-medium text-sm">{cardDetails.validUpto}</span>
</div>
</div>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-3 gap-4">
{/* Duration */}
<div className="bg-gray-50 rounded-lg p-4 text-center">
<div className="w-10 h-10 rounded-full bg-white shadow-sm flex items-center justify-center mx-auto mb-2">
<Clock className="w-5 h-5 text-primary" />
</div>
<div className="font-poppins text-xs text-gray-500 mb-1">Duration</div>
<div className="font-poppins font-semibold text-gray-900">{cardDetails.noOfDays}</div>
</div>
{/* Adults */}
<div className="bg-gray-50 rounded-lg p-4 text-center">
<div className="w-10 h-10 rounded-full bg-white shadow-sm flex items-center justify-center mx-auto mb-2">
<Users className="w-5 h-5 text-primary" />
</div>
<div className="font-poppins text-xs text-gray-500 mb-1">Adults</div>
<div className="font-poppins font-semibold text-gray-900">{cardDetails.totalAdult}</div>
</div>
{/* Kids */}
<div className="bg-gray-50 rounded-lg p-4 text-center">
<div className="w-10 h-10 rounded-full bg-white shadow-sm flex items-center justify-center mx-auto mb-2">
<Star className="w-5 h-5 text-primary" />
</div>
<div className="font-poppins text-xs text-gray-500 mb-1">Kids</div>
<div className="font-poppins font-semibold text-gray-900">{cardDetails.totalChild}</div>
</div>
</div>
</CardContent>
</Card>
</motion.div>
{/* Attractions Section - Redesigned */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="mb-8"
>
<Card className="border-0 shadow-lg">
<CardContent className="p-6">
{/* Section Header */}
<div className="flex items-center justify-between mb-5">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-primary to-secondary flex items-center justify-center">
<MapPin className="w-5 h-5 text-white" />
</div>
<div>
<h3 className="font-merchant text-lg font-semibold text-gray-900">
Included Attractions
</h3>
<p className="font-poppins text-xs text-gray-500">
Explore amazing places
</p>
</div>
</div>
<Badge className="bg-primary/10 text-primary border-0 px-3 py-1">
<span className="font-poppins font-medium">{attractions.length} {attractions.length > 1 ? "Places" : "Place"}</span>
</Badge>
</div>
{/* Attractions Scroll */}
<div className="flex items-center gap-4 overflow-x-auto pb-2 scrollbar-hide mb-4">
{attractions.map((attraction, index) => (
<motion.div
key={index}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.1 * index }}
className="flex-shrink-0"
>
<Card className="w-40 hover:shadow-lg transition-all duration-300 border-0 bg-gradient-to-br from-gray-50 to-white cursor-pointer group">
<CardContent className="p-3">
{/* Image */}
<div className="w-full h-24 bg-gray-200 rounded-lg mb-3 overflow-hidden">
<ImageWithFallback
src={attraction.image}
alt={attraction.title}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-300"
/>
</div>
{/* Info */}
<div>
<h4 className="font-poppins font-semibold text-sm text-gray-900 mb-1 truncate">
{attraction.title}
</h4>
<div className="flex items-center gap-1 text-xs text-primary">
<Ticket className="w-3 h-3" />
{/* <span className="font-poppins font-medium">{attraction.tours}</span> */}
</div>
</div>
</CardContent>
</Card>
</motion.div>
))}
</div>
{/* View All Button */}
<Button
variant="outline"
className="w-full border-2 border-dashed border-primary/30 hover:border-primary hover:bg-primary/5 text-primary font-poppins font-medium transition-all duration-300"
onClick={() => navigate("/attractions")}
>
<span>View All Attractions</span>
<ChevronRight className="w-4 h-4 ml-2" />
</Button>
</CardContent>
</Card>
</motion.div>
{/* Special Benefits & Offers - Redesigned */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="mb-8"
>
<Card className="border-0 shadow-lg overflow-hidden bg-gradient-to-br from-primary/5 via-white to-secondary/5">
<CardContent className="p-6">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-primary to-secondary flex items-center justify-center">
<Gift className="w-6 h-6 text-white" />
</div>
<div>
<h3 className="font-merchant text-xl font-semibold text-gray-900">
Exclusive Offers
</h3>
<p className="font-poppins text-sm text-gray-600 font-normal">
Special deals for cardholders
</p>
</div>
</div>
<Badge className="bg-gradient-to-r from-green-500 to-green-600 text-white border-0 px-3 py-1">
<Sparkles className="w-3 h-3 mr-1" />
<span className="font-poppins font-medium">Limited Time</span>
</Badge>
</div>
{/* Featured Offers Grid */}
<div className="space-y-4 mb-5">
{
offers.map((offer: any) => (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="bg-gradient-to-br from-orange-50 to-red-50 rounded-xl p-4 border border-orange-200 hover:border-orange-300 transition-all duration-300 cursor-pointer group hover:shadow-lg"
onClick={() => navigate(`/super-savings/${offer.id}`)}
>
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-orange-400 to-red-500 flex items-center justify-center">
<Gift className="w-5 h-5 text-white" />
</div>
<div>
<h4 className="font-poppins font-semibold text-base text-gray-900">
{offer.title}
</h4>
{/* <p className="font-poppins text-xs text-gray-600 font-normal">
{offer.description}
</p> */}
</div>
</div>
{/* <div className="bg-white rounded-lg px-3 py-2 shadow-sm">
<span className="font-merchant text-2xl font-semibold text-orange-600">25%</span>
<span className="font-poppins text-xs text-gray-600 ml-1">OFF</span>
</div> */}
</div>
<div className="flex items-center justify-between">
<p className="font-poppins text-xs text-gray-700 font-normal">
{offer.description}
</p>
<ChevronRight className="w-4 h-4 text-orange-600 group-hover:translate-x-1 transition-transform" />
</div>
</motion.div>
))
}
</div>
</CardContent>
</Card>
</motion.div>
</div>
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onContactUsClick={onContactUsClick}
currentPage={currentPage}
/>
</div>
);
}