Improve image handling, optimize hooks, and add rate limiting

This commit introduces several improvements:
- Enhances `RideModelCard` by safely accessing and displaying ride count and image data, preventing potential errors.
- Refactors `useEntityVersions` and `useSearch` hooks to use `useCallback` and improve performance and prevent race conditions.
- Introduces a `MAX_MAP_SIZE` and cleanup mechanism for the rate limiting map in `detect-location` Supabase function to prevent memory leaks.
- Adds robust error handling and cleanup for image uploads in `uploadPendingImages`.
- Modifies `ManufacturerModels` to correctly map and display ride counts.
- Includes error handling for topological sort in `process-selective-approval` Supabase function.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 39bb006b-d046-477f-a1f9-b7821836f3a1
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
pac7
2025-10-08 17:55:37 +00:00
parent b17d36de03
commit 0f2219f849
8 changed files with 165 additions and 76 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useMemo } from 'react';
import { useState, useEffect, useMemo, useCallback } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { Park, Ride, Company } from '@/types/database';
@@ -20,13 +20,23 @@ interface UseSearchOptions {
debounceMs?: number;
}
// Hoist default values to prevent recreating on every render
const DEFAULT_TYPES: ('park' | 'ride' | 'company')[] = ['park', 'ride', 'company'];
const DEFAULT_LIMIT = 10;
const DEFAULT_MIN_QUERY = 2;
const DEFAULT_DEBOUNCE_MS = 300;
export function useSearch(options: UseSearchOptions = {}) {
const {
types = ['park', 'ride', 'company'],
limit = 10,
minQuery = 2,
debounceMs = 300
} = options;
// Stabilize options using JSON stringify to prevent infinite loops from array recreation
const optionsKey = JSON.stringify({
types: options.types || DEFAULT_TYPES,
limit: options.limit || DEFAULT_LIMIT,
minQuery: options.minQuery || DEFAULT_MIN_QUERY,
debounceMs: options.debounceMs || DEFAULT_DEBOUNCE_MS
});
const stableOptions = useMemo(() => JSON.parse(optionsKey), [optionsKey]);
const { types, limit, minQuery, debounceMs } = stableOptions;
const [query, setQuery] = useState('');
const [results, setResults] = useState<SearchResult[]>([]);
@@ -61,7 +71,7 @@ export function useSearch(options: UseSearchOptions = {}) {
}, []);
// Search function
const search = async (searchQuery: string) => {
const search = useCallback(async (searchQuery: string) => {
if (searchQuery.length < minQuery) {
setResults([]);
return;
@@ -162,7 +172,7 @@ export function useSearch(options: UseSearchOptions = {}) {
} finally {
setLoading(false);
}
};
}, [types, limit, minQuery]);
// Effect for debounced search
useEffect(() => {
@@ -171,7 +181,7 @@ export function useSearch(options: UseSearchOptions = {}) {
} else {
setResults([]);
}
}, [debouncedQuery]);
}, [debouncedQuery, search]);
// Save search to recent searches
const saveSearch = (searchQuery: string) => {