Spaces:
Build error
Build error
import { useEffect, useState } from 'react'; | |
import { useParams, Link } from 'react-router-dom'; | |
import { getVehicleById, Vehicle } from '@/data/vehicles'; | |
import { ArrowLeft, Battery, Clock, Users, Zap, Check, Calendar, CreditCard } from 'lucide-react'; | |
const VehicleDetailPage = () => { | |
const { id } = useParams<{ id: string }>(); | |
const [vehicle, setVehicle] = useState<Vehicle | null>(null); | |
const [activeTab, setActiveTab] = useState<'overview' | 'specs' | 'features'>('overview'); | |
const [rentalDuration, setRentalDuration] = useState(1); // days | |
const [isLoading, setIsLoading] = useState(true); | |
useEffect(() => { | |
if (id) { | |
const foundVehicle = getVehicleById(id); | |
setVehicle(foundVehicle || null); | |
setIsLoading(false); | |
} | |
}, [id]); | |
if (isLoading) { | |
return ( | |
<div className="bg-[#0a0a0a] text-white min-h-screen flex items-center justify-center"> | |
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-500"></div> | |
</div> | |
); | |
} | |
if (!vehicle) { | |
return ( | |
<div className="bg-[#0a0a0a] text-white min-h-screen"> | |
<div className="container mx-auto px-4 py-12 text-center"> | |
<h2 className="text-2xl font-bold mb-4">Vehicle Not Found</h2> | |
<p className="text-gray-400 mb-6">Sorry, we couldn't find the vehicle you're looking for.</p> | |
<Link | |
to="/vehicles" | |
className="bg-blue-600 hover:bg-blue-700 px-5 py-2.5 rounded-md inline-flex items-center gap-2" | |
> | |
<ArrowLeft size={16} /> | |
Back to Vehicles | |
</Link> | |
</div> | |
</div> | |
); | |
} | |
const totalRentalPrice = vehicle.price * rentalDuration; | |
return ( | |
<div className="bg-[#0a0a0a] text-white min-h-screen"> | |
<div className="container mx-auto px-4 py-12"> | |
{/* Back navigation */} | |
<Link | |
to="/vehicles" | |
className="inline-flex items-center gap-2 text-gray-300 hover:text-white mb-8" | |
> | |
<ArrowLeft size={16} /> | |
Back to Vehicles | |
</Link> | |
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10"> | |
{/* Left Column - Image */} | |
<div> | |
<div className="bg-gradient-to-br from-[#15151a] to-[#0c0c10] p-8 rounded-lg h-full flex items-center justify-center"> | |
<img | |
src={vehicle.image} | |
alt={`${vehicle.name} ${vehicle.model}`} | |
className="w-full h-auto max-h-[400px] object-contain" | |
/> | |
</div> | |
</div> | |
{/* Right Column - Info */} | |
<div> | |
<div className="mb-6"> | |
<div className="flex items-start justify-between"> | |
<div> | |
<h1 className="text-3xl md:text-4xl font-bold"> | |
{vehicle.name} {vehicle.model} | |
</h1> | |
<div className="flex items-center gap-2 mt-2"> | |
<span className="inline-block px-2.5 py-1 bg-blue-600 text-xs font-semibold rounded-full"> | |
{vehicle.category} | |
</span> | |
<span className="text-gray-400">Electric</span> | |
</div> | |
</div> | |
<div className="text-right"> | |
<div className="text-3xl font-bold">${vehicle.price}</div> | |
<div className="text-gray-400 text-sm">per day</div> | |
</div> | |
</div> | |
<p className="text-gray-300 mt-4">{vehicle.description}</p> | |
</div> | |
{/* Tabs */} | |
<div className="border-b border-gray-800 mb-6"> | |
<nav className="flex gap-6"> | |
<button | |
onClick={() => setActiveTab('overview')} | |
className={`pb-3 px-1 relative ${ | |
activeTab === 'overview' | |
? 'text-blue-500 font-medium after:absolute after:h-0.5 after:w-full after:bg-blue-500 after:bottom-0 after:left-0' | |
: 'text-gray-400 hover:text-white' | |
}`} | |
> | |
Overview | |
</button> | |
<button | |
onClick={() => setActiveTab('specs')} | |
className={`pb-3 px-1 relative ${ | |
activeTab === 'specs' | |
? 'text-blue-500 font-medium after:absolute after:h-0.5 after:w-full after:bg-blue-500 after:bottom-0 after:left-0' | |
: 'text-gray-400 hover:text-white' | |
}`} | |
> | |
Specifications | |
</button> | |
<button | |
onClick={() => setActiveTab('features')} | |
className={`pb-3 px-1 relative ${ | |
activeTab === 'features' | |
? 'text-blue-500 font-medium after:absolute after:h-0.5 after:w-full after:bg-blue-500 after:bottom-0 after:left-0' | |
: 'text-gray-400 hover:text-white' | |
}`} | |
> | |
Features | |
</button> | |
</nav> | |
</div> | |
{/* Tab Content */} | |
<div className="mb-8"> | |
{activeTab === 'overview' && ( | |
<div> | |
<div className="grid grid-cols-2 gap-4 mb-6"> | |
<div className="bg-[#15151a] p-4 rounded-lg flex items-center gap-3"> | |
<div className="bg-blue-600/20 p-2 rounded"> | |
<Battery className="text-blue-500 w-5 h-5" /> | |
</div> | |
<div> | |
<div className="text-sm text-gray-400">Range</div> | |
<div className="font-medium">{vehicle.specs.range}</div> | |
</div> | |
</div> | |
<div className="bg-[#15151a] p-4 rounded-lg flex items-center gap-3"> | |
<div className="bg-blue-600/20 p-2 rounded"> | |
<Zap className="text-blue-500 w-5 h-5" /> | |
</div> | |
<div> | |
<div className="text-sm text-gray-400">Acceleration</div> | |
<div className="font-medium">{vehicle.specs.acceleration}</div> | |
</div> | |
</div> | |
<div className="bg-[#15151a] p-4 rounded-lg flex items-center gap-3"> | |
<div className="bg-blue-600/20 p-2 rounded"> | |
<Clock className="text-blue-500 w-5 h-5" /> | |
</div> | |
<div> | |
<div className="text-sm text-gray-400">Top Speed</div> | |
<div className="font-medium">{vehicle.specs.topSpeed}</div> | |
</div> | |
</div> | |
<div className="bg-[#15151a] p-4 rounded-lg flex items-center gap-3"> | |
<div className="bg-blue-600/20 p-2 rounded"> | |
<Users className="text-blue-500 w-5 h-5" /> | |
</div> | |
<div> | |
<div className="text-sm text-gray-400">Capacity</div> | |
<div className="font-medium">{vehicle.specs.capacity}</div> | |
</div> | |
</div> | |
</div> | |
<div className="mb-6"> | |
<h3 className="text-lg font-medium mb-2">Rental Options</h3> | |
<div className="flex gap-4 mb-4"> | |
<button | |
onClick={() => setRentalDuration(1)} | |
className={`flex-1 py-3 rounded-md transition-colors ${ | |
rentalDuration === 1 | |
? 'bg-blue-600 text-white' | |
: 'bg-[#15151a] text-gray-300 hover:bg-gray-800' | |
}`} | |
> | |
1 Day | |
</button> | |
<button | |
onClick={() => setRentalDuration(3)} | |
className={`flex-1 py-3 rounded-md transition-colors ${ | |
rentalDuration === 3 | |
? 'bg-blue-600 text-white' | |
: 'bg-[#15151a] text-gray-300 hover:bg-gray-800' | |
}`} | |
> | |
3 Days | |
</button> | |
<button | |
onClick={() => setRentalDuration(7)} | |
className={`flex-1 py-3 rounded-md transition-colors ${ | |
rentalDuration === 7 | |
? 'bg-blue-600 text-white' | |
: 'bg-[#15151a] text-gray-300 hover:bg-gray-800' | |
}`} | |
> | |
7 Days | |
</button> | |
</div> | |
</div> | |
<div className="bg-[#15151a] p-5 rounded-lg mb-6"> | |
<div className="flex justify-between mb-3"> | |
<span className="text-gray-300">${vehicle.price} × {rentalDuration} day{rentalDuration > 1 ? 's' : ''}</span> | |
<span>${totalRentalPrice}</span> | |
</div> | |
<div className="flex justify-between mb-3"> | |
<span className="text-gray-300">Insurance</span> | |
<span>Included</span> | |
</div> | |
<div className="flex justify-between mb-3"> | |
<span className="text-gray-300">Charging</span> | |
<span>Included</span> | |
</div> | |
<div className="border-t border-gray-800 pt-3 mt-3 flex justify-between font-bold"> | |
<span>Total</span> | |
<span>${totalRentalPrice}</span> | |
</div> | |
</div> | |
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4"> | |
<button className="bg-blue-600 hover:bg-blue-700 p-4 rounded-md font-medium transition-colors flex items-center justify-center gap-2"> | |
<Calendar size={18} /> | |
Rent Now | |
</button> | |
<button className="bg-gray-800 hover:bg-gray-700 p-4 rounded-md font-medium transition-colors flex items-center justify-center gap-2"> | |
<CreditCard size={18} /> | |
Subscribe for ${vehicle.pricePerMonth}/mo | |
</button> | |
</div> | |
</div> | |
)} | |
{activeTab === 'specs' && ( | |
<div> | |
<div className="bg-[#15151a] rounded-lg overflow-hidden"> | |
<div className="grid grid-cols-1 md:grid-cols-2"> | |
<div className="p-4 border-b md:border-b-0 md:border-r border-gray-800"> | |
<h3 className="font-medium mb-3">Performance</h3> | |
<div className="space-y-3"> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Range</span> | |
<span>{vehicle.specs.range}</span> | |
</div> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Top Speed</span> | |
<span>{vehicle.specs.topSpeed}</span> | |
</div> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Acceleration</span> | |
<span>{vehicle.specs.acceleration}</span> | |
</div> | |
</div> | |
</div> | |
<div className="p-4"> | |
<h3 className="font-medium mb-3">Interior</h3> | |
<div className="space-y-3"> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Seating</span> | |
<span>{vehicle.specs.capacity}</span> | |
</div> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Touchscreen</span> | |
<span>15" Center Display</span> | |
</div> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Sound System</span> | |
<span>Premium Audio</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div className="border-t border-gray-800"> | |
<div className="p-4"> | |
<h3 className="font-medium mb-3">Technology</h3> | |
<div className="space-y-3"> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Autopilot</span> | |
<span>Included</span> | |
</div> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Over-the-air Updates</span> | |
<span>Yes</span> | |
</div> | |
<div className="flex justify-between"> | |
<span className="text-gray-400">Connectivity</span> | |
<span>Premium Connectivity</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
)} | |
{activeTab === 'features' && ( | |
<div> | |
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
{vehicle.features.map((feature, index) => ( | |
<div key={index} className="flex items-center gap-2"> | |
<div className="flex-shrink-0 w-5 h-5 rounded-full bg-green-600/20 flex items-center justify-center"> | |
<Check className="text-green-500 w-3 h-3" /> | |
</div> | |
<span>{feature}</span> | |
</div> | |
))} | |
</div> | |
</div> | |
)} | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default VehicleDetailPage; | |