Files
thrilltrack-explorer/src-old/hooks/useAdvancedRideSearch.ts

185 lines
5.8 KiB
TypeScript

import { useState, useEffect } from 'react';
import { supabase } from '@/lib/supabaseClient';
import { TechnicalSpecFilter, CoasterStatFilter } from '@/components/search/AdvancedRideFilters';
import { useDebounce } from './useDebounce';
import { handleError } from '@/lib/errorHandler';
interface AdvancedSearchOptions {
query?: string;
category?: string;
manufacturer?: string;
technicalSpecFilters?: TechnicalSpecFilter[];
coasterStatFilters?: CoasterStatFilter[];
speedMin?: number;
speedMax?: number;
heightMin?: number;
heightMax?: number;
limit?: number;
}
export function useAdvancedRideSearch(options: AdvancedSearchOptions) {
const [results, setResults] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const debouncedQuery = useDebounce(options.query || '', 300);
useEffect(() => {
const performSearch = async () => {
if (!debouncedQuery && !options.category && !options.manufacturer &&
(!options.technicalSpecFilters || options.technicalSpecFilters.length === 0) &&
(!options.coasterStatFilters || options.coasterStatFilters.length === 0)) {
setResults([]);
return;
}
setLoading(true);
setError(null);
try {
let query = supabase
.from('rides')
.select(`
*,
parks!inner(id, name, slug),
companies!rides_manufacturer_id_fkey(id, name, slug)
`);
// Basic text search
if (debouncedQuery) {
query = query.or(`name.ilike.%${debouncedQuery}%,description.ilike.%${debouncedQuery}%`);
}
// Category filter
if (options.category) {
query = query.eq('category', options.category);
}
// Manufacturer filter
if (options.manufacturer) {
query = query.eq('manufacturer_id', options.manufacturer);
}
// Speed range filter
if (options.speedMin !== undefined) {
query = query.gte('max_speed_kmh', options.speedMin);
}
if (options.speedMax !== undefined) {
query = query.lte('max_speed_kmh', options.speedMax);
}
// Height range filter
if (options.heightMin !== undefined) {
query = query.gte('max_height_meters', options.heightMin);
}
if (options.heightMax !== undefined) {
query = query.lte('max_height_meters', options.heightMax);
}
query = query.limit(options.limit || 50);
const { data: initialResults, error: queryError } = await query;
if (queryError) throw queryError;
let filteredResults = initialResults || [];
// Apply technical specification filters
if (options.technicalSpecFilters && options.technicalSpecFilters.length > 0) {
for (const filter of options.technicalSpecFilters) {
if (!filter.spec_name) continue;
const { data: specsData, error: specsError } = await supabase
.from('ride_technical_specifications')
.select('ride_id, spec_name, spec_value')
.eq('spec_name', filter.spec_name);
if (specsError) throw specsError;
const matchingRideIds = new Set<string>();
specsData?.forEach((spec) => {
let matches = false;
switch (filter.operator) {
case 'has_spec':
matches = true;
break;
case 'equals':
matches = spec.spec_value === filter.spec_value;
break;
case 'contains':
matches = filter.spec_value ?
spec.spec_value.toLowerCase().includes(filter.spec_value.toLowerCase()) :
false;
break;
}
if (matches) {
matchingRideIds.add(spec.ride_id);
}
});
filteredResults = filteredResults.filter((ride) => matchingRideIds.has(ride.id));
}
}
// Apply coaster statistics filters
if (options.coasterStatFilters && options.coasterStatFilters.length > 0) {
for (const filter of options.coasterStatFilters) {
if (!filter.stat_name) continue;
let statsQuery = supabase
.from('ride_coaster_statistics')
.select('ride_id, stat_name, stat_value')
.eq('stat_name', filter.stat_name);
if (filter.min_value !== undefined) {
statsQuery = statsQuery.gte('stat_value', filter.min_value);
}
if (filter.max_value !== undefined) {
statsQuery = statsQuery.lte('stat_value', filter.max_value);
}
const { data: statsData, error: statsError } = await statsQuery;
if (statsError) throw statsError;
const matchingRideIds = new Set(statsData?.map((stat) => stat.ride_id) || []);
filteredResults = filteredResults.filter((ride) => matchingRideIds.has(ride.id));
}
}
setResults(filteredResults);
} catch (err) {
handleError(err, {
action: 'Advanced Ride Search',
metadata: {
query: options.query,
category: options.category,
manufacturer: options.manufacturer
}
});
setError(err instanceof Error ? err.message : 'Search failed');
setResults([]);
} finally {
setLoading(false);
}
};
performSearch();
}, [
debouncedQuery,
options.category,
options.manufacturer,
options.speedMin,
options.speedMax,
options.heightMin,
options.heightMax,
options.limit,
JSON.stringify(options.technicalSpecFilters),
JSON.stringify(options.coasterStatFilters),
]);
return { results, loading, error };
}