mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 21:51:17 -05:00
feat: Implement full Phase 3 API optimizations
This commit is contained in:
@@ -145,7 +145,7 @@ export function FeaturedParks() {
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
{topRatedParks.map((park) => (
|
||||
{topRated.data?.map((park) => (
|
||||
<FeaturedParkCard
|
||||
key={park.id}
|
||||
park={park}
|
||||
@@ -166,7 +166,7 @@ export function FeaturedParks() {
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
{mostRidesParks.map((park) => (
|
||||
{mostRides.data?.map((park) => (
|
||||
<FeaturedParkCard
|
||||
key={park.id}
|
||||
park={park}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useUserReviews } from '@/hooks/reviews/useUserReviews';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -43,62 +44,11 @@ interface UserReviewsListProps {
|
||||
}
|
||||
|
||||
export function UserReviewsList({ userId, reviewCount }: UserReviewsListProps) {
|
||||
const [reviews, setReviews] = useState<ReviewWithEntity[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [filter, setFilter] = useState<'all' | 'parks' | 'rides'>('all');
|
||||
const [sortBy, setSortBy] = useState<'date' | 'rating'>('date');
|
||||
|
||||
useEffect(() => {
|
||||
fetchReviews();
|
||||
}, [userId, filter, sortBy]);
|
||||
|
||||
const fetchReviews = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
let query = supabase
|
||||
.from('reviews')
|
||||
.select(`
|
||||
id,
|
||||
rating,
|
||||
title,
|
||||
content,
|
||||
visit_date,
|
||||
wait_time_minutes,
|
||||
helpful_votes,
|
||||
moderation_status,
|
||||
created_at,
|
||||
parks:park_id (id, name, slug),
|
||||
rides:ride_id (
|
||||
id,
|
||||
name,
|
||||
slug,
|
||||
parks:park_id (name, slug)
|
||||
)
|
||||
`)
|
||||
.eq('user_id', userId);
|
||||
|
||||
if (filter === 'parks') {
|
||||
query = query.not('park_id', 'is', null);
|
||||
} else if (filter === 'rides') {
|
||||
query = query.not('ride_id', 'is', null);
|
||||
}
|
||||
|
||||
if (sortBy === 'date') {
|
||||
query = query.order('created_at', { ascending: false });
|
||||
} else {
|
||||
query = query.order('rating', { ascending: false });
|
||||
}
|
||||
|
||||
const { data, error } = await query;
|
||||
|
||||
if (error) throw error;
|
||||
setReviews(data || []);
|
||||
} catch (error: unknown) {
|
||||
toast.error(getErrorMessage(error));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// Use cached user reviews hook
|
||||
const { data: reviews = [], isLoading: loading } = useUserReviews(userId, filter, sortBy);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
|
||||
import { useGlobalSearch } from '@/hooks/search/useGlobalSearch';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -20,57 +22,20 @@ type SearchResult = {
|
||||
};
|
||||
|
||||
export function SearchResults({ query, onClose }: SearchResultsProps) {
|
||||
const [results, setResults] = useState<SearchResult[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (query.length >= 2) {
|
||||
searchContent();
|
||||
} else {
|
||||
setResults([]);
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
const searchContent = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const searchTerm = `%${query.toLowerCase()}%`;
|
||||
|
||||
// Search parks
|
||||
const { data: parks } = await supabase
|
||||
.from('parks')
|
||||
.select(`*, location:locations(*)`)
|
||||
.or(`name.ilike.${searchTerm},description.ilike.${searchTerm}`)
|
||||
.limit(5);
|
||||
|
||||
// Search rides
|
||||
const { data: rides } = await supabase
|
||||
.from('rides')
|
||||
.select(`*, park:parks!inner(name, slug)`)
|
||||
.or(`name.ilike.${searchTerm},description.ilike.${searchTerm}`)
|
||||
.limit(5);
|
||||
|
||||
// Search companies
|
||||
const { data: companies } = await supabase
|
||||
.from('companies')
|
||||
.select('id, name, slug, description, company_type, logo_url, average_rating, review_count')
|
||||
.or(`name.ilike.${searchTerm},description.ilike.${searchTerm}`)
|
||||
.limit(3);
|
||||
|
||||
const allResults: SearchResult[] = [
|
||||
...(parks || []).map(park => ({ type: 'park' as const, data: park })),
|
||||
...(rides || []).map(ride => ({ type: 'ride' as const, data: ride })),
|
||||
...(companies || []).map(company => ({ type: 'company' as const, data: company }))
|
||||
];
|
||||
|
||||
setResults(allResults);
|
||||
} catch (error: unknown) {
|
||||
logger.error('Search failed', { error: getErrorMessage(error), query });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Debounce search query
|
||||
const debouncedQuery = useDebouncedValue(query, 300);
|
||||
|
||||
// Use global search hook with caching
|
||||
const { data, isLoading: loading } = useGlobalSearch(debouncedQuery);
|
||||
|
||||
// Flatten results
|
||||
const results: SearchResult[] = [
|
||||
...(data?.parks || []).map(park => ({ type: 'park' as const, data: park })),
|
||||
...(data?.rides || []).map(ride => ({ type: 'ride' as const, data: ride })),
|
||||
...(data?.companies || []).map(company => ({ type: 'company' as const, data: company })),
|
||||
];
|
||||
|
||||
const handleResultClick = (result: SearchResult) => {
|
||||
onClose();
|
||||
|
||||
Reference in New Issue
Block a user