mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 07:51:13 -05:00
189 lines
6.9 KiB
TypeScript
189 lines
6.9 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { Star, TrendingUp, Award, Castle, FerrisWheel, Waves, Tent, LucideIcon } from 'lucide-react';
|
|
import { formatLocationShort } from '@/lib/locationFormatter';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Park } from '@/types/database';
|
|
import { supabase } from '@/lib/supabaseClient';
|
|
import { getErrorMessage } from '@/lib/errorHandler';
|
|
|
|
export function FeaturedParks() {
|
|
const [topRatedParks, setTopRatedParks] = useState<Park[]>([]);
|
|
const [mostRidesParks, setMostRidesParks] = useState<Park[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
fetchFeaturedParks();
|
|
}, []);
|
|
|
|
const fetchFeaturedParks = async () => {
|
|
try {
|
|
// Fetch top rated parks
|
|
const { data: topRated } = await supabase
|
|
.from('parks')
|
|
.select(`
|
|
*,
|
|
location:locations(*),
|
|
operator:companies!parks_operator_id_fkey(*)
|
|
`)
|
|
.order('average_rating', { ascending: false })
|
|
.limit(3);
|
|
|
|
// Fetch parks with most rides
|
|
const { data: mostRides } = await supabase
|
|
.from('parks')
|
|
.select(`
|
|
*,
|
|
location:locations(*),
|
|
operator:companies!parks_operator_id_fkey(*)
|
|
`)
|
|
.order('ride_count', { ascending: false })
|
|
.limit(3);
|
|
|
|
setTopRatedParks(topRated || []);
|
|
setMostRidesParks(mostRides || []);
|
|
} catch (error: unknown) {
|
|
// Featured parks fetch failed - display empty sections
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const FeaturedParkCard = ({ park, icon: Icon, label }: { park: Park; icon: LucideIcon; label: string }) => (
|
|
<Card className="group overflow-hidden border-border/50 bg-gradient-to-br from-card via-card to-card/80 hover:shadow-xl hover:shadow-primary/10 transition-all duration-300 cursor-pointer hover:scale-[1.02]">
|
|
<div className="relative">
|
|
{/* Gradient Background */}
|
|
<div className="aspect-video bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 flex items-center justify-center relative">
|
|
<div className="opacity-50">
|
|
{park.park_type === 'theme_park' ? <Castle className="w-16 h-16" /> :
|
|
park.park_type === 'amusement_park' ? <FerrisWheel className="w-16 h-16" /> :
|
|
park.park_type === 'water_park' ? <Waves className="w-16 h-16" /> : <Tent className="w-16 h-16" />}
|
|
</div>
|
|
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent" />
|
|
|
|
{/* Featured Badge */}
|
|
<Badge className="absolute top-3 left-3 bg-primary/90 text-primary-foreground border-0">
|
|
<Icon className="w-3 h-3 mr-1" />
|
|
{label}
|
|
</Badge>
|
|
|
|
{/* Rating Badge */}
|
|
<Badge className="absolute top-3 right-3 bg-background/90 text-foreground border-0">
|
|
<Star className="w-3 h-3 mr-1 fill-yellow-400 text-yellow-400" />
|
|
{park.average_rating ? park.average_rating.toFixed(1) : 'N/A'}
|
|
</Badge>
|
|
</div>
|
|
|
|
<CardContent className="p-4">
|
|
<div className="space-y-2">
|
|
<h3 className="font-bold text-lg group-hover:text-primary transition-colors line-clamp-1">
|
|
{park.name}
|
|
</h3>
|
|
|
|
{park.location && (
|
|
<p className="text-sm text-muted-foreground">
|
|
{formatLocationShort(park.location)}
|
|
</p>
|
|
)}
|
|
|
|
<div className="flex items-center justify-between text-sm">
|
|
<div className="flex items-center gap-3">
|
|
<span className="text-primary font-medium">{park.ride_count} rides</span>
|
|
<div className="flex items-center gap-1">
|
|
<span className="text-accent font-medium">{park.coaster_count}</span>
|
|
<FerrisWheel className="w-3 h-3 text-accent" />
|
|
</div>
|
|
</div>
|
|
<div className="text-xs text-muted-foreground">
|
|
{park.review_count} reviews
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</div>
|
|
</Card>
|
|
);
|
|
|
|
if (loading) {
|
|
return (
|
|
<section className="py-12">
|
|
<div className="container mx-auto px-4">
|
|
<div className="animate-pulse space-y-6">
|
|
<div className="h-8 bg-muted rounded w-1/3"></div>
|
|
<div className="grid md:grid-cols-3 gap-6">
|
|
{[...Array(6)].map((_, i) => (
|
|
<div key={i} className="h-64 bg-muted rounded-lg"></div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<section className="py-16 bg-muted/20">
|
|
<div className="container mx-auto px-4">
|
|
<div className="text-center max-w-3xl mx-auto mb-12">
|
|
<h2 className="text-3xl md:text-4xl font-bold mb-4">
|
|
Featured
|
|
<span className="bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent"> Destinations</span>
|
|
</h2>
|
|
<p className="text-xl text-muted-foreground">
|
|
Discover the highest-rated parks and thrill capitals around the world
|
|
</p>
|
|
</div>
|
|
|
|
{/* Top Rated Parks */}
|
|
<div className="mb-12">
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<div className="w-8 h-8 bg-primary/20 rounded-full flex items-center justify-center">
|
|
<Award className="w-4 h-4 text-primary" />
|
|
</div>
|
|
<h3 className="text-2xl font-bold">Top Rated Parks</h3>
|
|
</div>
|
|
|
|
<div className="grid md:grid-cols-3 gap-6">
|
|
{topRatedParks.map((park) => (
|
|
<FeaturedParkCard
|
|
key={park.id}
|
|
park={park}
|
|
icon={Award}
|
|
label="Top Rated"
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Most Rides */}
|
|
<div>
|
|
<div className="flex items-center gap-3 mb-6">
|
|
<div className="w-8 h-8 bg-secondary/20 rounded-full flex items-center justify-center">
|
|
<TrendingUp className="w-4 h-4 text-secondary" />
|
|
</div>
|
|
<h3 className="text-2xl font-bold">Thrill Capitals</h3>
|
|
</div>
|
|
|
|
<div className="grid md:grid-cols-3 gap-6">
|
|
{mostRidesParks.map((park) => (
|
|
<FeaturedParkCard
|
|
key={park.id}
|
|
park={park}
|
|
icon={TrendingUp}
|
|
label="Most Rides"
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Call to Action */}
|
|
<div className="text-center mt-12">
|
|
<Button size="lg" variant="outline" className="border-primary/30 hover:bg-primary/10">
|
|
Explore All Parks
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
} |