/** * Inventory hook for managing inventory state and operations */ import { useState, useEffect, useCallback } from 'react'; import { InventoryService } from '../../services/api/inventory.service'; import { Ingredient, IngredientCreate, IngredientUpdate, StockLevel, StockMovement, StockMovementCreate, InventoryAlert, QualityCheckCreate, QualityCheck } from '../../types/inventory.types'; import { ApiResponse, PaginatedResponse, QueryParams } from '../../types/api.types'; interface InventoryState { ingredients: Ingredient[]; stockLevels: StockLevel[]; stockMovements: StockMovement[]; alerts: InventoryAlert[]; qualityChecks: QualityCheck[]; isLoading: boolean; error: string | null; pagination: { total: number; page: number; pages: number; limit: number; }; } interface InventoryActions { // Ingredients fetchIngredients: (params?: QueryParams) => Promise; createIngredient: (data: IngredientCreate) => Promise; updateIngredient: (id: string, data: IngredientUpdate) => Promise; deleteIngredient: (id: string) => Promise; getIngredient: (id: string) => Promise; // Stock Levels fetchStockLevels: (params?: QueryParams) => Promise; updateStockLevel: (ingredientId: string, quantity: number, reason?: string) => Promise; // Stock Movements fetchStockMovements: (params?: QueryParams) => Promise; createStockMovement: (data: StockMovementCreate) => Promise; // Alerts fetchAlerts: (params?: QueryParams) => Promise; markAlertAsRead: (id: string) => Promise; dismissAlert: (id: string) => Promise; // Quality Checks fetchQualityChecks: (params?: QueryParams) => Promise; createQualityCheck: (data: QualityCheckCreate) => Promise; // Analytics getInventoryAnalytics: (startDate?: string, endDate?: string) => Promise; getExpirationReport: () => Promise; // Utilities clearError: () => void; refresh: () => Promise; } export const useInventory = (): InventoryState & InventoryActions => { const [state, setState] = useState({ ingredients: [], stockLevels: [], stockMovements: [], alerts: [], qualityChecks: [], isLoading: false, error: null, pagination: { total: 0, page: 1, pages: 1, limit: 20, }, }); const inventoryService = new InventoryService(); // Fetch ingredients const fetchIngredients = useCallback(async (params?: QueryParams) => { setState(prev => ({ ...prev, isLoading: true, error: null })); try { const response = await inventoryService.getIngredients(params); if (response.success && response.data) { setState(prev => ({ ...prev, ingredients: Array.isArray(response.data) ? response.data : response.data.items || [], pagination: response.data.pagination || prev.pagination, isLoading: false, })); } else { setState(prev => ({ ...prev, isLoading: false, error: response.error || 'Error al cargar ingredientes', })); } } catch (error) { setState(prev => ({ ...prev, isLoading: false, error: 'Error de conexión al servidor', })); } }, [inventoryService]); // Create ingredient const createIngredient = useCallback(async (data: IngredientCreate): Promise => { setState(prev => ({ ...prev, error: null })); try { const response = await inventoryService.createIngredient(data); if (response.success) { await fetchIngredients(); return true; } else { setState(prev => ({ ...prev, error: response.error || 'Error al crear ingrediente', })); return false; } } catch (error) { setState(prev => ({ ...prev, error: 'Error de conexión al servidor', })); return false; } }, [inventoryService, fetchIngredients]); // Update ingredient const updateIngredient = useCallback(async (id: string, data: IngredientUpdate): Promise => { setState(prev => ({ ...prev, error: null })); try { const response = await inventoryService.updateIngredient(id, data); if (response.success) { await fetchIngredients(); return true; } else { setState(prev => ({ ...prev, error: response.error || 'Error al actualizar ingrediente', })); return false; } } catch (error) { setState(prev => ({ ...prev, error: 'Error de conexión al servidor', })); return false; } }, [inventoryService, fetchIngredients]); // Delete ingredient const deleteIngredient = useCallback(async (id: string): Promise => { setState(prev => ({ ...prev, error: null })); try { const response = await inventoryService.deleteIngredient(id); if (response.success) { setState(prev => ({ ...prev, ingredients: prev.ingredients.filter(ingredient => ingredient.id !== id), })); return true; } else { setState(prev => ({ ...prev, error: response.error || 'Error al eliminar ingrediente', })); return false; } } catch (error) { setState(prev => ({ ...prev, error: 'Error de conexión al servidor', })); return false; } }, [inventoryService]); // Get single ingredient const getIngredient = useCallback(async (id: string): Promise => { try { const response = await inventoryService.getIngredient(id); return response.success ? response.data : null; } catch (error) { console.error('Error fetching ingredient:', error); return null; } }, [inventoryService]); // Fetch stock levels const fetchStockLevels = useCallback(async (params?: QueryParams) => { setState(prev => ({ ...prev, isLoading: true, error: null })); try { const response = await inventoryService.getStockLevels(params); if (response.success && response.data) { setState(prev => ({ ...prev, stockLevels: Array.isArray(response.data) ? response.data : response.data.items || [], isLoading: false, })); } else { setState(prev => ({ ...prev, isLoading: false, error: response.error || 'Error al cargar niveles de stock', })); } } catch (error) { setState(prev => ({ ...prev, isLoading: false, error: 'Error de conexión al servidor', })); } }, [inventoryService]); // Update stock level const updateStockLevel = useCallback(async (ingredientId: string, quantity: number, reason?: string): Promise => { setState(prev => ({ ...prev, error: null })); try { const response = await inventoryService.updateStockLevel(ingredientId, { quantity, reason: reason || 'Manual adjustment' }); if (response.success) { await fetchStockLevels(); return true; } else { setState(prev => ({ ...prev, error: response.error || 'Error al actualizar stock', })); return false; } } catch (error) { setState(prev => ({ ...prev, error: 'Error de conexión al servidor', })); return false; } }, [inventoryService, fetchStockLevels]); // Fetch stock movements const fetchStockMovements = useCallback(async (params?: QueryParams) => { setState(prev => ({ ...prev, isLoading: true, error: null })); try { const response = await inventoryService.getStockMovements(params); if (response.success && response.data) { setState(prev => ({ ...prev, stockMovements: Array.isArray(response.data) ? response.data : response.data.items || [], isLoading: false, })); } else { setState(prev => ({ ...prev, isLoading: false, error: response.error || 'Error al cargar movimientos de stock', })); } } catch (error) { setState(prev => ({ ...prev, isLoading: false, error: 'Error de conexión al servidor', })); } }, [inventoryService]); // Create stock movement const createStockMovement = useCallback(async (data: StockMovementCreate): Promise => { setState(prev => ({ ...prev, error: null })); try { const response = await inventoryService.createStockMovement(data); if (response.success) { await fetchStockMovements(); await fetchStockLevels(); return true; } else { setState(prev => ({ ...prev, error: response.error || 'Error al crear movimiento de stock', })); return false; } } catch (error) { setState(prev => ({ ...prev, error: 'Error de conexión al servidor', })); return false; } }, [inventoryService, fetchStockMovements, fetchStockLevels]); // Fetch alerts const fetchAlerts = useCallback(async (params?: QueryParams) => { try { const response = await inventoryService.getAlerts(params); if (response.success && response.data) { setState(prev => ({ ...prev, alerts: Array.isArray(response.data) ? response.data : response.data.items || [], })); } } catch (error) { console.error('Error fetching alerts:', error); } }, [inventoryService]); // Mark alert as read const markAlertAsRead = useCallback(async (id: string): Promise => { try { const response = await inventoryService.markAlertAsRead(id); if (response.success) { setState(prev => ({ ...prev, alerts: prev.alerts.map(alert => alert.id === id ? { ...alert, is_read: true } : alert ), })); return true; } return false; } catch (error) { console.error('Error marking alert as read:', error); return false; } }, [inventoryService]); // Dismiss alert const dismissAlert = useCallback(async (id: string): Promise => { try { const response = await inventoryService.dismissAlert(id); if (response.success) { setState(prev => ({ ...prev, alerts: prev.alerts.filter(alert => alert.id !== id), })); return true; } return false; } catch (error) { console.error('Error dismissing alert:', error); return false; } }, [inventoryService]); // Fetch quality checks const fetchQualityChecks = useCallback(async (params?: QueryParams) => { try { const response = await inventoryService.getQualityChecks(params); if (response.success && response.data) { setState(prev => ({ ...prev, qualityChecks: Array.isArray(response.data) ? response.data : response.data.items || [], })); } } catch (error) { console.error('Error fetching quality checks:', error); } }, [inventoryService]); // Create quality check const createQualityCheck = useCallback(async (data: QualityCheckCreate): Promise => { try { const response = await inventoryService.createQualityCheck(data); if (response.success) { await fetchQualityChecks(); return true; } return false; } catch (error) { console.error('Error creating quality check:', error); return false; } }, [inventoryService, fetchQualityChecks]); // Get inventory analytics const getInventoryAnalytics = useCallback(async (startDate?: string, endDate?: string) => { try { const response = await inventoryService.getAnalytics(startDate, endDate); return response.success ? response.data : null; } catch (error) { console.error('Error fetching inventory analytics:', error); return null; } }, [inventoryService]); // Get expiration report const getExpirationReport = useCallback(async () => { try { const response = await inventoryService.getExpirationReport(); return response.success ? response.data : null; } catch (error) { console.error('Error fetching expiration report:', error); return null; } }, [inventoryService]); // Clear error const clearError = useCallback(() => { setState(prev => ({ ...prev, error: null })); }, []); // Refresh all data const refresh = useCallback(async () => { await Promise.all([ fetchIngredients(), fetchStockLevels(), fetchAlerts(), ]); }, [fetchIngredients, fetchStockLevels, fetchAlerts]); // Initialize data on mount useEffect(() => { refresh(); }, []); return { ...state, fetchIngredients, createIngredient, updateIngredient, deleteIngredient, getIngredient, fetchStockLevels, updateStockLevel, fetchStockMovements, createStockMovement, fetchAlerts, markAlertAsRead, dismissAlert, fetchQualityChecks, createQualityCheck, getInventoryAnalytics, getExpirationReport, clearError, refresh, }; };