Update deployment health checks. Fix UI bugs.
This commit is contained in:
@@ -40,9 +40,11 @@ export function useOptimisticVehicles(vehicles: Vehicle[] = []) {
|
||||
};
|
||||
|
||||
// Sync optimistic state with actual vehicles when they change
|
||||
// Guard: Skip if both are empty arrays (prevents infinite loop on reference changes)
|
||||
useEffect(() => {
|
||||
if (vehicles.length === 0 && optimisticVehicles.length === 0) return;
|
||||
setOptimisticVehicles(vehicles);
|
||||
}, [vehicles]);
|
||||
}, [vehicles, optimisticVehicles.length]);
|
||||
|
||||
const optimisticCreateVehicle = async (data: CreateVehicleRequest) => {
|
||||
// Create optimistic vehicle with temporary ID
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @ai-context Non-blocking updates for better UI responsiveness
|
||||
*/
|
||||
|
||||
import { useTransition, useState, useCallback } from 'react';
|
||||
import { useTransition, useState, useCallback, useEffect } from 'react';
|
||||
import { Vehicle } from '../types/vehicles.types';
|
||||
|
||||
export function useVehicleSearch(vehicles: Vehicle[]) {
|
||||
@@ -42,35 +42,32 @@ export function useVehicleSearch(vehicles: Vehicle[]) {
|
||||
});
|
||||
}, [vehicles]);
|
||||
|
||||
// Update filtered vehicles when vehicles data changes
|
||||
const updateVehicles = useCallback((newVehicles: Vehicle[]) => {
|
||||
startTransition(() => {
|
||||
if (!searchQuery.trim()) {
|
||||
setFilteredVehicles(newVehicles);
|
||||
} else {
|
||||
// Re-apply search filter to new data
|
||||
const filtered = newVehicles.filter(vehicle => {
|
||||
const searchTerm = searchQuery.toLowerCase();
|
||||
return (
|
||||
vehicle.nickname?.toLowerCase().includes(searchTerm) ||
|
||||
vehicle.make?.toLowerCase().includes(searchTerm) ||
|
||||
vehicle.model?.toLowerCase().includes(searchTerm) ||
|
||||
vehicle.vin.toLowerCase().includes(searchTerm) ||
|
||||
vehicle.year?.toString().includes(searchTerm)
|
||||
);
|
||||
});
|
||||
setFilteredVehicles(filtered);
|
||||
}
|
||||
});
|
||||
}, [searchQuery]);
|
||||
// Auto-sync filtered vehicles when source vehicles change
|
||||
// Note: Direct state update (no startTransition) to avoid deferred render cascades
|
||||
useEffect(() => {
|
||||
if (!searchQuery.trim()) {
|
||||
setFilteredVehicles(vehicles);
|
||||
} else {
|
||||
const filtered = vehicles.filter(vehicle => {
|
||||
const searchTerm = searchQuery.toLowerCase();
|
||||
return (
|
||||
vehicle.nickname?.toLowerCase().includes(searchTerm) ||
|
||||
vehicle.make?.toLowerCase().includes(searchTerm) ||
|
||||
vehicle.model?.toLowerCase().includes(searchTerm) ||
|
||||
vehicle.vin.toLowerCase().includes(searchTerm) ||
|
||||
vehicle.year?.toString().includes(searchTerm)
|
||||
);
|
||||
});
|
||||
setFilteredVehicles(filtered);
|
||||
}
|
||||
}, [vehicles, searchQuery]);
|
||||
|
||||
return {
|
||||
searchQuery,
|
||||
filteredVehicles,
|
||||
isPending,
|
||||
handleSearch,
|
||||
clearSearch,
|
||||
updateVehicles
|
||||
clearSearch
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @ai-context Enhanced with Suspense, optimistic updates, and transitions
|
||||
*/
|
||||
|
||||
import React, { useTransition, useEffect } from 'react';
|
||||
import React, { useTransition, useMemo } from 'react';
|
||||
import { Box, Typography, Grid, Button } from '@mui/material';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import { useVehicles } from '../hooks/useVehicles';
|
||||
@@ -40,24 +40,22 @@ export const VehiclesMobileScreen: React.FC<VehiclesMobileScreenProps> = ({
|
||||
}) => {
|
||||
const { data: vehicles, isLoading } = useVehicles();
|
||||
const [_isPending, startTransition] = useTransition();
|
||||
|
||||
|
||||
// Stable reference for empty array (prevents infinite loop when vehicles is undefined)
|
||||
const safeVehicles = useMemo(
|
||||
() => (Array.isArray(vehicles) ? vehicles : []),
|
||||
[vehicles]
|
||||
);
|
||||
|
||||
// React 19 optimistic updates
|
||||
const {
|
||||
optimisticVehicles,
|
||||
isPending: isOptimisticPending
|
||||
} = useOptimisticVehicles(Array.isArray(vehicles) ? vehicles : []);
|
||||
|
||||
// Enhanced search with transitions
|
||||
const {
|
||||
filteredVehicles,
|
||||
updateVehicles
|
||||
} = useVehicleSearch(optimisticVehicles);
|
||||
|
||||
// Update search when optimistic vehicles change
|
||||
useEffect(() => {
|
||||
updateVehicles(optimisticVehicles);
|
||||
}, [optimisticVehicles, updateVehicles]);
|
||||
} = useOptimisticVehicles(safeVehicles);
|
||||
|
||||
// Enhanced search with transitions (auto-syncs when vehicles change)
|
||||
const { filteredVehicles } = useVehicleSearch(optimisticVehicles);
|
||||
|
||||
const handleVehicleSelect = (vehicle: Vehicle) => {
|
||||
// Use transition to avoid blocking UI during navigation
|
||||
startTransition(() => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @ai-context Enhanced with Suspense, useOptimistic, and useTransition
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useTransition } from 'react';
|
||||
import React, { useState, useTransition, useMemo } from 'react';
|
||||
import { Box, Typography, Grid, Button as MuiButton, TextField, IconButton } from '@mui/material';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
@@ -25,33 +25,33 @@ export const VehiclesPage: React.FC = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { data: vehicles, isLoading } = useVehicles();
|
||||
const setSelectedVehicle = useAppStore(state => state.setSelectedVehicle);
|
||||
|
||||
|
||||
// Stable reference for empty array (prevents infinite loop when vehicles is undefined)
|
||||
const safeVehicles = useMemo(
|
||||
() => (Array.isArray(vehicles) ? vehicles : []),
|
||||
[vehicles]
|
||||
);
|
||||
|
||||
// React 19 optimistic updates and transitions
|
||||
const {
|
||||
optimisticVehicles,
|
||||
isPending: isOptimisticPending,
|
||||
optimisticCreateVehicle,
|
||||
optimisticDeleteVehicle
|
||||
} = useOptimisticVehicles(Array.isArray(vehicles) ? vehicles : []);
|
||||
const {
|
||||
optimisticVehicles,
|
||||
isPending: isOptimisticPending,
|
||||
optimisticCreateVehicle,
|
||||
optimisticDeleteVehicle
|
||||
} = useOptimisticVehicles(safeVehicles);
|
||||
|
||||
const {
|
||||
searchQuery,
|
||||
filteredVehicles,
|
||||
isPending: isSearchPending,
|
||||
handleSearch,
|
||||
clearSearch,
|
||||
updateVehicles
|
||||
const {
|
||||
searchQuery,
|
||||
filteredVehicles,
|
||||
isPending: isSearchPending,
|
||||
handleSearch,
|
||||
clearSearch
|
||||
} = useVehicleSearch(optimisticVehicles);
|
||||
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [stagedImageFile, setStagedImageFile] = useState<File | null>(null);
|
||||
|
||||
// Update search vehicles when optimistic vehicles change
|
||||
useEffect(() => {
|
||||
updateVehicles(optimisticVehicles);
|
||||
}, [optimisticVehicles, updateVehicles]);
|
||||
|
||||
const handleSelectVehicle = (id: string) => {
|
||||
// Use transition for navigation to avoid blocking UI
|
||||
startTransition(() => {
|
||||
|
||||
Reference in New Issue
Block a user