mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 17:31:14 -05:00
Refactor: Implement optimistic updates
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
@@ -139,16 +139,76 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreditAdded = () => {
|
||||
fetchCredits();
|
||||
setIsAddDialogOpen(false);
|
||||
const handleCreditAdded = async (newCreditId: string) => {
|
||||
try {
|
||||
// Fetch just the new credit with all necessary joins
|
||||
const { data, error } = await supabase
|
||||
.from('user_ride_credits')
|
||||
.select(`
|
||||
*,
|
||||
rides:ride_id (
|
||||
id,
|
||||
name,
|
||||
slug,
|
||||
category,
|
||||
status,
|
||||
coaster_type,
|
||||
seating_type,
|
||||
intensity_level,
|
||||
max_speed_kmh,
|
||||
max_height_meters,
|
||||
length_meters,
|
||||
inversions,
|
||||
card_image_url,
|
||||
parks:park_id (
|
||||
id,
|
||||
name,
|
||||
slug,
|
||||
park_type,
|
||||
locations:location_id (
|
||||
country,
|
||||
state_province,
|
||||
city
|
||||
)
|
||||
),
|
||||
manufacturer:manufacturer_id (
|
||||
id,
|
||||
name
|
||||
),
|
||||
designer:designer_id (
|
||||
id,
|
||||
name
|
||||
)
|
||||
)
|
||||
`)
|
||||
.eq('id', newCreditId)
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
// Add to existing array
|
||||
setCredits(prev => [...prev, data as any]);
|
||||
setIsAddDialogOpen(false);
|
||||
} catch (error) {
|
||||
console.error('Error fetching new credit:', error);
|
||||
toast.error(getErrorMessage(error));
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreditUpdated = () => {
|
||||
fetchCredits();
|
||||
const handleCreditUpdated = (creditId: string, updates: Partial<UserRideCredit>) => {
|
||||
// Optimistic update - update only the affected credit
|
||||
setCredits(prev => prev.map(c =>
|
||||
c.id === creditId ? { ...c, ...updates } : c
|
||||
));
|
||||
};
|
||||
|
||||
const handleCreditDeleted = async (creditId: string) => {
|
||||
// Store for rollback
|
||||
const deletedCredit = credits.find(c => c.id === creditId);
|
||||
|
||||
// Optimistic removal
|
||||
setCredits(prev => prev.filter(c => c.id !== creditId));
|
||||
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('user_ride_credits')
|
||||
@@ -159,10 +219,14 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) {
|
||||
if (error) throw error;
|
||||
|
||||
toast.success('Ride credit removed');
|
||||
fetchCredits();
|
||||
} catch (error) {
|
||||
console.error('Error deleting credit:', error);
|
||||
toast.error(getErrorMessage(error));
|
||||
|
||||
// Rollback on error
|
||||
if (deletedCredit) {
|
||||
setCredits(prev => [...prev, deletedCredit]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -175,8 +239,7 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) {
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
// Refetch to get accurate sort_order values
|
||||
await fetchCredits();
|
||||
// No refetch - optimistic update is already applied
|
||||
} catch (error) {
|
||||
console.error('Error reordering credit:', error);
|
||||
throw error;
|
||||
@@ -193,6 +256,9 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) {
|
||||
|
||||
if (oldIndex === -1 || newIndex === -1) return;
|
||||
|
||||
// Store old order for rollback
|
||||
const oldCredits = credits;
|
||||
|
||||
// Optimistic update
|
||||
const newCredits = arrayMove(credits, oldIndex, newIndex);
|
||||
setCredits(newCredits);
|
||||
@@ -202,17 +268,18 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) {
|
||||
toast.success('Order updated');
|
||||
} catch (error) {
|
||||
toast.error(getErrorMessage(error));
|
||||
// Revert on error
|
||||
fetchCredits();
|
||||
// Rollback to old order
|
||||
setCredits(oldCredits);
|
||||
}
|
||||
};
|
||||
|
||||
const stats = {
|
||||
// Memoize statistics to only recalculate when credits change
|
||||
const stats = useMemo(() => ({
|
||||
totalRides: credits.reduce((sum, c) => sum + c.ride_count, 0),
|
||||
uniqueCredits: credits.length,
|
||||
parksVisited: new Set(credits.map(c => c.rides?.parks?.id).filter(Boolean)).size,
|
||||
coasters: credits.filter(c => c.rides?.category === 'roller_coaster').length
|
||||
};
|
||||
}), [credits]);
|
||||
|
||||
// Use filtered credits for display
|
||||
const displayCredits = filteredCredits;
|
||||
|
||||
Reference in New Issue
Block a user