Approve database migration

This commit is contained in:
gpt-engineer-app[bot]
2025-10-16 15:38:28 +00:00
parent bd44597f9a
commit 0d9926a5ae
9 changed files with 1027 additions and 116 deletions

View File

@@ -0,0 +1,234 @@
import { useState, useMemo, useCallback } from 'react';
import { useDebounce } from '@/hooks/useDebounce';
import { RideCreditFilters, FilterPreset } from '@/types/ride-credits';
import { UserRideCredit } from '@/types/database';
export function useRideCreditFilters(credits: UserRideCredit[]) {
const [filters, setFilters] = useState<RideCreditFilters>({});
const debouncedSearchQuery = useDebounce(filters.searchQuery || '', 300);
const updateFilter = useCallback((key: keyof RideCreditFilters, value: any) => {
setFilters(prev => ({ ...prev, [key]: value }));
}, []);
const clearFilters = useCallback(() => {
setFilters({});
}, []);
const applyPreset = useCallback((preset: FilterPreset) => {
switch (preset) {
case 'mostRidden':
setFilters({ minRideCount: 10 });
break;
case 'recentlyAdded':
// Will be sorted by date
setFilters({});
break;
case 'singleRides':
setFilters({ minRideCount: 1, maxRideCount: 1 });
break;
case 'needRating':
setFilters({ hasRating: false });
break;
case 'highlyRated':
setFilters({ hasRating: true, minUserRating: 4 });
break;
case 'all':
default:
clearFilters();
break;
}
}, [clearFilters]);
const filteredCredits = useMemo(() => {
let result = [...credits];
// Search filter
if (debouncedSearchQuery) {
const search = debouncedSearchQuery.toLowerCase();
result = result.filter(credit =>
credit.rides?.name?.toLowerCase().includes(search) ||
credit.rides?.parks?.name?.toLowerCase().includes(search)
);
}
// Categories
if (filters.categories && filters.categories.length > 0) {
result = result.filter(credit =>
filters.categories!.includes(credit.rides?.category || '')
);
}
// Geographic filters
if (filters.countries && filters.countries.length > 0) {
result = result.filter(credit =>
filters.countries!.includes(credit.rides?.parks?.locations?.country || '')
);
}
if (filters.statesProvinces && filters.statesProvinces.length > 0) {
result = result.filter(credit =>
filters.statesProvinces!.includes(credit.rides?.parks?.locations?.state_province || '')
);
}
if (filters.cities && filters.cities.length > 0) {
result = result.filter(credit =>
filters.cities!.includes(credit.rides?.parks?.locations?.city || '')
);
}
// Park filters
if (filters.parks && filters.parks.length > 0) {
result = result.filter(credit =>
filters.parks!.includes(credit.rides?.parks?.id || '')
);
}
if (filters.parkTypes && filters.parkTypes.length > 0) {
result = result.filter(credit =>
filters.parkTypes!.includes(credit.rides?.parks?.park_type || '')
);
}
// Manufacturers
if (filters.manufacturers && filters.manufacturers.length > 0) {
result = result.filter(credit =>
credit.rides?.manufacturer?.id &&
filters.manufacturers!.includes(credit.rides.manufacturer.id)
);
}
// Ride count range
if (filters.minRideCount !== undefined) {
result = result.filter(credit => credit.ride_count >= filters.minRideCount!);
}
if (filters.maxRideCount !== undefined) {
result = result.filter(credit => credit.ride_count <= filters.maxRideCount!);
}
// Speed range
if (filters.minSpeed !== undefined || filters.maxSpeed !== undefined) {
result = result.filter(credit => {
const speed = credit.rides?.max_speed_kmh;
if (!speed) return false;
if (filters.minSpeed && speed < filters.minSpeed) return false;
if (filters.maxSpeed && speed > filters.maxSpeed) return false;
return true;
});
}
// Height range
if (filters.minHeight !== undefined || filters.maxHeight !== undefined) {
result = result.filter(credit => {
const height = credit.rides?.max_height_meters;
if (!height) return false;
if (filters.minHeight && height < filters.minHeight) return false;
if (filters.maxHeight && height > filters.maxHeight) return false;
return true;
});
}
// Inversions
if (filters.hasInversions !== undefined) {
result = result.filter(credit => {
const inversions = credit.rides?.inversions || 0;
return filters.hasInversions ? inversions > 0 : inversions === 0;
});
}
if (filters.minInversions !== undefined) {
result = result.filter(credit => {
const inversions = credit.rides?.inversions || 0;
return inversions >= filters.minInversions!;
});
}
// User rating
if (filters.hasRating === true) {
result = result.filter(credit => credit.personal_rating !== null);
} else if (filters.hasRating === false) {
result = result.filter(credit => credit.personal_rating === null);
}
if (filters.minUserRating !== undefined) {
result = result.filter(credit =>
(credit.personal_rating || 0) >= filters.minUserRating!
);
}
// Notes
if (filters.hasNotes === true) {
result = result.filter(credit =>
credit.personal_notes && credit.personal_notes.trim().length > 0
);
} else if (filters.hasNotes === false) {
result = result.filter(credit =>
!credit.personal_notes || credit.personal_notes.trim().length === 0
);
}
// Photos
if (filters.hasPhotos === true) {
result = result.filter(credit => credit.personal_photo_id !== null);
} else if (filters.hasPhotos === false) {
result = result.filter(credit => credit.personal_photo_id === null);
}
// Date ranges
if (filters.firstRideDateFrom) {
result = result.filter(credit =>
credit.first_ride_date &&
new Date(credit.first_ride_date) >= filters.firstRideDateFrom!
);
}
if (filters.firstRideDateTo) {
result = result.filter(credit =>
credit.first_ride_date &&
new Date(credit.first_ride_date) <= filters.firstRideDateTo!
);
}
if (filters.lastRideDateFrom) {
result = result.filter(credit =>
credit.last_ride_date &&
new Date(credit.last_ride_date) >= filters.lastRideDateFrom!
);
}
if (filters.lastRideDateTo) {
result = result.filter(credit =>
credit.last_ride_date &&
new Date(credit.last_ride_date) <= filters.lastRideDateTo!
);
}
// Ride status
if (filters.rideStatuses && filters.rideStatuses.length > 0) {
result = result.filter(credit =>
filters.rideStatuses!.includes(credit.rides?.status || '')
);
}
return result;
}, [credits, filters, debouncedSearchQuery]);
const activeFilterCount = useMemo(() => {
return Object.entries(filters).filter(([_, value]) => {
if (Array.isArray(value)) return value.length > 0;
if (typeof value === 'boolean') return true;
return value !== undefined && value !== null && value !== '';
}).length;
}, [filters]);
return {
filters,
updateFilter,
clearFilters,
applyPreset,
filteredCredits,
activeFilterCount,
};
}