mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 15:11:13 -05:00
feat: Add recent changes and recently opened tabs
This commit is contained in:
@@ -2,6 +2,8 @@ import { useState, useEffect } from 'react';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { ParkCard } from '@/components/parks/ParkCard';
|
||||
import { RideCard } from '@/components/rides/RideCard';
|
||||
import { RecentChangeCard } from './RecentChangeCard';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Park, Ride } from '@/types/database';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
|
||||
@@ -10,6 +12,8 @@ export function ContentTabs() {
|
||||
const [trendingRides, setTrendingRides] = useState<Ride[]>([]);
|
||||
const [recentParks, setRecentParks] = useState<Park[]>([]);
|
||||
const [recentRides, setRecentRides] = useState<Ride[]>([]);
|
||||
const [recentChanges, setRecentChanges] = useState<any[]>([]);
|
||||
const [recentlyOpened, setRecentlyOpened] = useState<Array<Park | Ride>>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -46,10 +50,69 @@ export function ContentTabs() {
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(12);
|
||||
|
||||
// Fetch recent entity changes
|
||||
const { data: changesData } = await supabase
|
||||
.from('entity_versions')
|
||||
.select(`
|
||||
id,
|
||||
entity_type,
|
||||
entity_id,
|
||||
version_number,
|
||||
version_data,
|
||||
changed_at,
|
||||
change_type,
|
||||
change_reason,
|
||||
changer_profile:profiles!entity_versions_changed_by_fkey(username, avatar_url)
|
||||
`)
|
||||
.order('changed_at', { ascending: false })
|
||||
.limit(24);
|
||||
|
||||
// Process changes to extract entity info from version_data
|
||||
const processedChanges = changesData?.map(change => {
|
||||
const versionData = change.version_data as any;
|
||||
return {
|
||||
...change,
|
||||
entity_name: versionData?.name || 'Unknown',
|
||||
entity_slug: versionData?.slug || '',
|
||||
entity_image_url: versionData?.card_image_url || versionData?.banner_image_url,
|
||||
};
|
||||
}) || [];
|
||||
|
||||
// Fetch recently opened parks and rides
|
||||
const oneYearAgo = new Date();
|
||||
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
||||
const dateThreshold = oneYearAgo.toISOString().split('T')[0];
|
||||
|
||||
const { data: openedParks } = await supabase
|
||||
.from('parks')
|
||||
.select(`*, location:locations(*), operator:companies!parks_operator_id_fkey(*)`)
|
||||
.not('opening_date', 'is', null)
|
||||
.gte('opening_date', dateThreshold)
|
||||
.order('opening_date', { ascending: false })
|
||||
.limit(20);
|
||||
|
||||
const { data: openedRides } = await supabase
|
||||
.from('rides')
|
||||
.select(`*, park:parks!inner(name, slug, location:locations(*))`)
|
||||
.not('opening_date', 'is', null)
|
||||
.gte('opening_date', dateThreshold)
|
||||
.order('opening_date', { ascending: false })
|
||||
.limit(20);
|
||||
|
||||
// Combine and sort by opening date
|
||||
const combinedOpened = [
|
||||
...(openedParks || []).map(p => ({ ...p, entityType: 'park' as const })),
|
||||
...(openedRides || []).map(r => ({ ...r, entityType: 'ride' as const }))
|
||||
]
|
||||
.sort((a, b) => new Date(b.opening_date).getTime() - new Date(a.opening_date).getTime())
|
||||
.slice(0, 24);
|
||||
|
||||
setTrendingParks(trending || []);
|
||||
setRecentParks(recent || []);
|
||||
setTrendingRides(trendingRidesData || []);
|
||||
setRecentRides(recentRidesData || []);
|
||||
setRecentChanges(processedChanges);
|
||||
setRecentlyOpened(combinedOpened);
|
||||
} catch (error) {
|
||||
console.error('Error fetching content:', error);
|
||||
} finally {
|
||||
@@ -80,7 +143,7 @@ export function ContentTabs() {
|
||||
<div className="container mx-auto px-4">
|
||||
<Tabs defaultValue="trending-parks" className="w-full">
|
||||
<div className="text-center mb-8">
|
||||
<TabsList className="grid w-full max-w-3xl mx-auto grid-cols-2 md:grid-cols-4 h-auto p-2 bg-gradient-to-r from-primary/10 via-secondary/10 to-accent/10 dark:from-primary/20 dark:via-secondary/20 dark:to-accent/20 dark:bg-card/50 border border-primary/20 dark:border-primary/30 rounded-xl shadow-lg backdrop-blur-sm">
|
||||
<TabsList className="grid w-full max-w-5xl mx-auto grid-cols-3 md:grid-cols-6 h-auto p-2 bg-gradient-to-r from-primary/10 via-secondary/10 to-accent/10 dark:from-primary/20 dark:via-secondary/20 dark:to-accent/20 dark:bg-card/50 border border-primary/20 dark:border-primary/30 rounded-xl shadow-lg backdrop-blur-sm">
|
||||
<TabsTrigger value="trending-parks" className="text-xs md:text-sm px-2 py-3">
|
||||
Trending Parks
|
||||
</TabsTrigger>
|
||||
@@ -93,6 +156,12 @@ export function ContentTabs() {
|
||||
<TabsTrigger value="recent-rides" className="text-xs md:text-sm px-2 py-3">
|
||||
New Rides
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="recent-changes" className="text-xs md:text-sm px-2 py-3">
|
||||
Recent Changes
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="recently-opened" className="text-xs md:text-sm px-2 py-3">
|
||||
Recently Opened
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
@@ -143,6 +212,56 @@ export function ContentTabs() {
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="recent-changes" className="mt-8">
|
||||
<div className="text-center mb-6">
|
||||
<h2 className="text-2xl font-bold mb-2">Recent Changes</h2>
|
||||
<p className="text-muted-foreground">Latest updates across all entities</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-4">
|
||||
{recentChanges.map((change) => (
|
||||
<RecentChangeCard
|
||||
key={change.id}
|
||||
entityType={change.entity_type}
|
||||
entityId={change.entity_id}
|
||||
entityName={change.entity_name}
|
||||
entitySlug={change.entity_slug}
|
||||
imageUrl={change.entity_image_url}
|
||||
changeType={change.change_type}
|
||||
changedAt={change.changed_at}
|
||||
changedByUsername={change.changer_profile?.username}
|
||||
changedByAvatar={change.changer_profile?.avatar_url}
|
||||
changeReason={change.change_reason}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="recently-opened" className="mt-8">
|
||||
<div className="text-center mb-6">
|
||||
<h2 className="text-2xl font-bold mb-2">Recently Opened</h2>
|
||||
<p className="text-muted-foreground">Parks and rides that opened in the last year</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-7 3xl:grid-cols-8 gap-4 lg:gap-5 xl:gap-4 2xl:gap-5">
|
||||
{recentlyOpened.map((entity: any) => (
|
||||
entity.entityType === 'park' ? (
|
||||
<div key={entity.id} className="relative">
|
||||
<ParkCard park={entity} />
|
||||
<Badge className="absolute top-2 right-2 bg-green-500/90 text-white backdrop-blur-sm">
|
||||
{new Date(entity.opening_date).getFullYear()}
|
||||
</Badge>
|
||||
</div>
|
||||
) : (
|
||||
<div key={entity.id} className="relative">
|
||||
<RideCard ride={entity} />
|
||||
<Badge className="absolute top-2 right-2 bg-green-500/90 text-white backdrop-blur-sm">
|
||||
{new Date(entity.opening_date).getFullYear()}
|
||||
</Badge>
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user