'use client' import { useState, useCallback, useTransition } from 'react' interface UseOptimisticUpdateOptions { onUpdate: (data: T) => Promise onError?: (error: Error) => void } interface UseOptimisticUpdateReturn { optimisticData: T | null isPending: boolean error: Error | null performUpdate: (data: T) => Promise reset: () => void } /** * Custom hook for optimistic UI updates * Provides immediate UI feedback while server action is processing * Validates: Requirements 7.3 */ export function useOptimisticUpdate( options: UseOptimisticUpdateOptions ): UseOptimisticUpdateReturn { const { onUpdate, onError } = options const [isPending, startTransition] = useTransition() const [optimisticData, setOptimisticData] = useState(null) const [error, setError] = useState(null) const performUpdate = useCallback(async (data: T) => { // Clear any previous errors setError(null) // Set optimistic data immediately for instant UI feedback setOptimisticData(data) // Perform the actual update in a transition startTransition(async () => { try { await onUpdate(data) // Clear optimistic data after successful update setOptimisticData(null) } catch (err) { const error = err instanceof Error ? err : new Error('Update failed') setError(error) // Clear optimistic data on error setOptimisticData(null) if (onError) { onError(error) } } }) }, [onUpdate, onError]) const reset = useCallback(() => { setOptimisticData(null) setError(null) }, []) return { optimisticData, isPending, error, performUpdate, reset } } /** * Simplified hook for optimistic list updates * Useful for managing lists of items with optimistic updates */ export function useOptimisticList( initialData: T[], onUpdate: (data: T[]) => Promise ) { const [optimisticList, setOptimisticList] = useState(initialData) const [isPending, startTransition] = useTransition() const addItem = useCallback((item: T) => { setOptimisticList(prev => [...prev, item]) startTransition(async () => { try { await onUpdate([...optimisticList, item]) } catch (error) { // Rollback on error setOptimisticList(initialData) console.error('Failed to add item:', error) } }) }, [optimisticList, initialData, onUpdate]) const updateItem = useCallback((id: string, updates: Partial) => { setOptimisticList(prev => prev.map(item => (item.id === id ? { ...item, ...updates } : item)) ) startTransition(async () => { try { const updatedList = optimisticList.map(item => item.id === id ? { ...item, ...updates } : item ) await onUpdate(updatedList) } catch (error) { // Rollback on error setOptimisticList(initialData) console.error('Failed to update item:', error) } }) }, [optimisticList, initialData, onUpdate]) const removeItem = useCallback((id: string) => { setOptimisticList(prev => prev.filter(item => item.id !== id)) startTransition(async () => { try { const updatedList = optimisticList.filter(item => item.id !== id) await onUpdate(updatedList) } catch (error) { // Rollback on error setOptimisticList(initialData) console.error('Failed to remove item:', error) } }) }, [optimisticList, initialData, onUpdate]) return { optimisticList, isPending, addItem, updateItem, removeItem } }