From 995a51e285520324e8a488cc4f64f77e11937495 Mon Sep 17 00:00:00 2001 From: Urtzi Alfaro Date: Sat, 16 Aug 2025 08:43:35 +0200 Subject: [PATCH] Fix new services implementation 11 --- frontend/src/App.tsx | 6 +- frontend/src/api/client/index.ts | 7 ++ .../src/api/services/inventory.service.ts | 48 +++++++-- frontend/src/api/types/data.ts | 4 + frontend/src/components/layout/Layout.tsx | 4 +- frontend/src/hooks/useDashboard.ts | 1 + frontend/src/hooks/useOrderSuggestions.ts | 102 ++++++++---------- .../src/pages/dashboard/DashboardPage.tsx | 13 ++- 8 files changed, 114 insertions(+), 71 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f44940d8..2633c969 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -15,6 +15,7 @@ import ForecastPage from './pages/forecast/ForecastPage'; import OrdersPage from './pages/orders/OrdersPage'; import InventoryPage from './pages/inventory/InventoryPage'; import SalesPage from './pages/sales/SalesPage'; +import RecipesPage from './pages/recipes/RecipesPage'; import SettingsPage from './pages/settings/SettingsPage'; import Layout from './components/layout/Layout'; @@ -31,7 +32,7 @@ import './i18n'; // Global styles import './styles/globals.css'; -type CurrentPage = 'landing' | 'login' | 'register' | 'onboarding' | 'dashboard' | 'reports' | 'orders' | 'production' | 'inventory' | 'sales' | 'settings'; +type CurrentPage = 'landing' | 'login' | 'register' | 'onboarding' | 'dashboard' | 'reports' | 'orders' | 'production' | 'inventory' | 'recipes' | 'sales' | 'settings'; interface User { id: string; @@ -295,6 +296,8 @@ const App: React.FC = () => { return ; case 'inventory': return ; + case 'recipes': + return ; case 'sales': return ; case 'settings': @@ -305,6 +308,7 @@ const App: React.FC = () => { onNavigateToReports={() => navigateTo('reports')} onNavigateToProduction={() => navigateTo('production')} onNavigateToInventory={() => navigateTo('inventory')} + onNavigateToRecipes={() => navigateTo('recipes')} onNavigateToSales={() => navigateTo('sales')} />; } diff --git a/frontend/src/api/client/index.ts b/frontend/src/api/client/index.ts index fb96c043..23959b2b 100644 --- a/frontend/src/api/client/index.ts +++ b/frontend/src/api/client/index.ts @@ -304,9 +304,11 @@ private buildURL(endpoint: string): string { } const responseData = await response.json(); + console.log('๐Ÿ” Raw responseData from fetch:', responseData); // Apply response interceptors const processedResponse = await this.applyResponseInterceptors(responseData); + console.log('๐Ÿ” processedResponse after interceptors:', processedResponse); return processedResponse; }); @@ -335,9 +337,14 @@ private buildURL(endpoint: string): string { // Handle both wrapped and unwrapped responses // If result has a 'data' property, return it; otherwise return the result itself + console.log('๐Ÿ” Final result before return:', result); + console.log('๐Ÿ” Result has data property?', result && typeof result === 'object' && 'data' in result); + if (result && typeof result === 'object' && 'data' in result) { + console.log('๐Ÿ” Returning result.data:', result.data); return result.data as T; } + console.log('๐Ÿ” Returning raw result:', result); return result as T; } catch (error) { // Record error metrics diff --git a/frontend/src/api/services/inventory.service.ts b/frontend/src/api/services/inventory.service.ts index 51b87680..f4b12f59 100644 --- a/frontend/src/api/services/inventory.service.ts +++ b/frontend/src/api/services/inventory.service.ts @@ -550,27 +550,46 @@ export class InventoryService { const response = await apiClient.get(`/tenants/${tenantId}/ingredients`, { params: { limit: 100, - product_type: 'finished_product' // Only get finished products, not raw ingredients + product_type: 'finished_product' }, }); console.log('๐Ÿ” Inventory Products API Response:', response); + console.log('๐Ÿ” Raw response data:', response.data); + console.log('๐Ÿ” Response status:', response.status); + console.log('๐Ÿ” Response headers:', response.headers); + console.log('๐Ÿ” Full response object keys:', Object.keys(response || {})); + console.log('๐Ÿ” Response data type:', typeof response); + console.log('๐Ÿ” Response data constructor:', response?.constructor?.name); + + // Check if response.data exists and what type it is + if (response && 'data' in response) { + console.log('๐Ÿ” Response.data exists:', typeof response.data); + console.log('๐Ÿ” Response.data keys:', Object.keys(response.data || {})); + console.log('๐Ÿ” Response.data constructor:', response.data?.constructor?.name); + } let productsArray: any[] = []; - if (Array.isArray(response)) { - productsArray = response; - } else if (response && typeof response === 'object') { + // Check response.data first (typical API client behavior) + const dataToProcess = response?.data || response; + + if (Array.isArray(dataToProcess)) { + productsArray = dataToProcess; + console.log('โœ… Found array data with', productsArray.length, 'items'); + } else if (dataToProcess && typeof dataToProcess === 'object') { // Handle different response formats - const keys = Object.keys(response); + const keys = Object.keys(dataToProcess); if (keys.length > 0 && keys.every(key => !isNaN(Number(key)))) { - productsArray = Object.values(response); + productsArray = Object.values(dataToProcess); + console.log('โœ… Found object with numeric keys, converted to array with', productsArray.length, 'items'); } else { - console.warn('โš ๏ธ Response is object but not with numeric keys:', response); + console.warn('โš ๏ธ Response is object but not with numeric keys:', dataToProcess); + console.warn('โš ๏ธ Object keys:', keys); return []; } } else { - console.warn('โš ๏ธ Response is not array or object:', response); + console.warn('โš ๏ธ Response data is not array or object:', dataToProcess); return []; } @@ -593,8 +612,19 @@ export class InventoryService { } catch (error) { console.error('โŒ Failed to fetch inventory products:', error); + console.error('โŒ Error details:', { + message: error instanceof Error ? error.message : 'Unknown error', + response: (error as any)?.response, + status: (error as any)?.response?.status, + data: (error as any)?.response?.data + }); - // Return empty array on error - let dashboard handle fallback + // If it's an authentication error, throw it to trigger auth flow + if ((error as any)?.response?.status === 401) { + throw error; + } + + // Return empty array on other errors - let dashboard handle fallback return []; } } diff --git a/frontend/src/api/types/data.ts b/frontend/src/api/types/data.ts index a13891e6..fb3dfac0 100644 --- a/frontend/src/api/types/data.ts +++ b/frontend/src/api/types/data.ts @@ -12,6 +12,10 @@ export interface ProductInfo { sales_count?: number; total_quantity?: number; last_sale_date?: string; + // Additional inventory fields + current_stock?: number; + unit?: string; + cost_per_unit?: number; } export interface SalesData { diff --git a/frontend/src/components/layout/Layout.tsx b/frontend/src/components/layout/Layout.tsx index 063cf18b..0991a02c 100644 --- a/frontend/src/components/layout/Layout.tsx +++ b/frontend/src/components/layout/Layout.tsx @@ -12,7 +12,8 @@ import { ChevronDown, ChefHat, Warehouse, - ShoppingCart + ShoppingCart, + BookOpen } from 'lucide-react'; interface LayoutProps { @@ -44,6 +45,7 @@ const Layout: React.FC = ({ { id: 'dashboard', label: 'Inicio', icon: Home, href: '/dashboard' }, { id: 'orders', label: 'Pedidos', icon: Package, href: '/orders' }, { id: 'production', label: 'Producciรณn', icon: ChefHat, href: '/production' }, + { id: 'recipes', label: 'Recetas', icon: BookOpen, href: '/recipes' }, { id: 'inventory', label: 'Inventario', icon: Warehouse, href: '/inventory' }, { id: 'sales', label: 'Ventas', icon: ShoppingCart, href: '/sales' }, { id: 'reports', label: 'Informes', icon: TrendingUp, href: '/reports' }, diff --git a/frontend/src/hooks/useDashboard.ts b/frontend/src/hooks/useDashboard.ts index 610683c7..b4bb6b7d 100644 --- a/frontend/src/hooks/useDashboard.ts +++ b/frontend/src/hooks/useDashboard.ts @@ -224,6 +224,7 @@ export const useDashboard = () => { return { ...dashboardData, + tenantId, isLoading: isLoading || salesLoading || inventoryLoading || externalLoading || forecastLoading, error: error || salesError || inventoryError || externalError || forecastError, reload: () => tenantId ? loadDashboardData(tenantId) : Promise.resolve(), diff --git a/frontend/src/hooks/useOrderSuggestions.ts b/frontend/src/hooks/useOrderSuggestions.ts index 432f28da..a17618d0 100644 --- a/frontend/src/hooks/useOrderSuggestions.ts +++ b/frontend/src/hooks/useOrderSuggestions.ts @@ -32,13 +32,16 @@ const SUPPLIERS: Record = { 'Bolsas papel': 'Distribuciones Madrid', }; -export const useOrderSuggestions = () => { +export const useOrderSuggestions = (providedTenantId?: string | null) => { const [dailyOrders, setDailyOrders] = useState([]); const [weeklyOrders, setWeeklyOrders] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); - const { tenantId, isLoading: tenantLoading, error: tenantError } = useTenantId(); + const { tenantId: hookTenantId, isLoading: tenantLoading, error: tenantError } = useTenantId(); + + // Use provided tenant ID if available, otherwise use hook tenant ID + const tenantId = providedTenantId !== undefined ? providedTenantId : hookTenantId; console.log('๐Ÿข OrderSuggestions: Tenant info:', { tenantId, tenantLoading, tenantError }); const { @@ -65,43 +68,32 @@ export const useOrderSuggestions = () => { console.log('๐Ÿ“Š OrderSuggestions: Generating daily suggestions for tenant:', tenantId); // Get products list from backend - let products: string[] = []; - try { - products = await getProductsList(tenantId); - console.log('๐Ÿ“‹ OrderSuggestions: Products list:', products); - } catch (error) { - console.error('โŒ OrderSuggestions: Failed to get products list:', error); - throw error; - } + const productsList = await getProductsList(tenantId); + const products = productsList.map(p => p.name); + console.log('๐Ÿ“‹ OrderSuggestions: Products list:', products); + // Filter for daily bakery products (case insensitive) + const dailyProductKeywords = ['pan', 'baguette', 'croissant', 'magdalena']; const dailyProducts = products.filter(p => - ['Pan de Molde', 'Baguettes', 'Croissants', 'Magdalenas'].includes(p) + dailyProductKeywords.some(keyword => + p.toLowerCase().includes(keyword.toLowerCase()) + ) ); console.log('๐Ÿฅ– OrderSuggestions: Daily products:', dailyProducts); // Get quick forecasts for these products - let quickForecasts: any[] = []; - try { - quickForecasts = await getQuickForecasts(tenantId); - console.log('๐Ÿ”ฎ OrderSuggestions: Quick forecasts:', quickForecasts); - } catch (error) { - console.error('โŒ OrderSuggestions: Failed to get quick forecasts:', error); - throw error; - } + const quickForecasts = await getQuickForecasts(tenantId); + console.log('๐Ÿ”ฎ OrderSuggestions: Quick forecasts:', quickForecasts); // Get weather data to determine urgency - let weather: any = null; - try { - weather = await getCurrentWeather(tenantId, 40.4168, -3.7038); - console.log('๐ŸŒค๏ธ OrderSuggestions: Weather data:', weather); - } catch (error) { - console.error('โŒ OrderSuggestions: Failed to get current weather:', error); - throw error; - } + const weather = await getCurrentWeather(tenantId, 40.4168, -3.7038); + console.log('๐ŸŒค๏ธ OrderSuggestions: Weather data:', weather); const suggestions: DailyOrderItem[] = []; + console.log('๐Ÿ”„ OrderSuggestions: Processing daily products:', dailyProducts); for (const product of dailyProducts) { + console.log('๐Ÿ”„ OrderSuggestions: Processing product:', product); // Find forecast for this product const forecast = quickForecasts.find(f => f.product_name === product || f.inventory_product_id === product @@ -145,14 +137,15 @@ export const useOrderSuggestions = () => { }; suggestions.push(orderItem); + console.log('โž• OrderSuggestions: Added daily suggestion:', orderItem); } } + console.log('๐ŸŽฏ OrderSuggestions: Final daily suggestions:', suggestions); return suggestions; } catch (error) { - console.error('โŒ OrderSuggestions: Error generating daily suggestions, using fallback:', error); - // Return mock data as fallback - return getMockDailyOrders(); + console.error('โŒ OrderSuggestions: Error in generateDailyOrderSuggestions:', error); + return []; } }, [tenantId, getProductsList, getQuickForecasts, getCurrentWeather]); @@ -160,21 +153,14 @@ export const useOrderSuggestions = () => { const generateWeeklyOrderSuggestions = useCallback(async (): Promise => { if (!tenantId) return []; - try { - console.log('๐Ÿ“Š OrderSuggestions: Generating weekly suggestions for tenant:', tenantId); + console.log('๐Ÿ“Š OrderSuggestions: Generating weekly suggestions for tenant:', tenantId); // Get sales analytics for the past month const endDate = new Date().toISOString(); const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString(); - let analytics: any = null; - try { - analytics = await getSalesAnalytics(tenantId, startDate, endDate); - console.log('๐Ÿ“ˆ OrderSuggestions: Sales analytics:', analytics); - } catch (error) { - console.error('โŒ OrderSuggestions: Failed to get sales analytics:', error); - throw error; - } + const analytics = await getSalesAnalytics(tenantId, startDate, endDate); + console.log('๐Ÿ“ˆ OrderSuggestions: Sales analytics:', analytics); // Weekly products (ingredients and supplies) const weeklyProducts = [ @@ -221,11 +207,6 @@ export const useOrderSuggestions = () => { } return suggestions.sort((a, b) => a.stockDays - b.stockDays); // Sort by urgency - } catch (error) { - console.error('โŒ OrderSuggestions: Error generating weekly suggestions, using fallback:', error); - // Return mock data as fallback - return getMockWeeklyOrders(); - } }, [tenantId, getSalesAnalytics]); // Load order suggestions @@ -233,10 +214,7 @@ export const useOrderSuggestions = () => { console.log('๐Ÿ” OrderSuggestions: loadOrderSuggestions called, tenantId:', tenantId); if (!tenantId) { - console.log('โŒ OrderSuggestions: No tenantId available, loading mock data'); - // Load mock data when tenant ID is not available - setDailyOrders(getMockDailyOrders()); - setWeeklyOrders(getMockWeeklyOrders()); + console.log('โŒ OrderSuggestions: No tenantId available, skipping load'); return; } @@ -245,15 +223,21 @@ export const useOrderSuggestions = () => { try { console.log('๐Ÿ“Š OrderSuggestions: Starting to generate suggestions...'); + console.log('๐Ÿ“Š OrderSuggestions: About to call generateDailyOrderSuggestions'); - const [daily, weekly] = await Promise.all([ - generateDailyOrderSuggestions(), - generateWeeklyOrderSuggestions() - ]); + const dailyPromise = generateDailyOrderSuggestions(); + console.log('๐Ÿ“Š OrderSuggestions: About to call generateWeeklyOrderSuggestions'); + + const weeklyPromise = generateWeeklyOrderSuggestions(); + console.log('๐Ÿ“Š OrderSuggestions: Waiting for both promises to resolve...'); + + const [daily, weekly] = await Promise.all([dailyPromise, weeklyPromise]); console.log('โœ… OrderSuggestions: Generated suggestions:', { dailyCount: daily.length, - weeklyCount: weekly.length + weeklyCount: weekly.length, + dailyData: daily, + weeklyData: weekly }); setDailyOrders(daily); @@ -268,9 +252,13 @@ export const useOrderSuggestions = () => { // Load on mount and when tenant changes useEffect(() => { - console.log('๐Ÿ”„ OrderSuggestions: useEffect triggered, tenantId:', tenantId); - loadOrderSuggestions(); - }, [loadOrderSuggestions]); + console.log('๐Ÿ”„ OrderSuggestions: useEffect triggered, tenantId:', tenantId, 'tenantLoading:', tenantLoading); + + // Only load if we have a tenantId or if tenant loading is complete + if (tenantId || !tenantLoading) { + loadOrderSuggestions(); + } + }, [tenantId, tenantLoading, loadOrderSuggestions]); return { dailyOrders, diff --git a/frontend/src/pages/dashboard/DashboardPage.tsx b/frontend/src/pages/dashboard/DashboardPage.tsx index 6fd16474..acbec11a 100644 --- a/frontend/src/pages/dashboard/DashboardPage.tsx +++ b/frontend/src/pages/dashboard/DashboardPage.tsx @@ -15,15 +15,22 @@ interface DashboardPageProps { onNavigateToOrders?: () => void; onNavigateToReports?: () => void; onNavigateToProduction?: () => void; + onNavigateToInventory?: () => void; + onNavigateToRecipes?: () => void; + onNavigateToSales?: () => void; } const DashboardPage: React.FC = ({ onNavigateToOrders, onNavigateToReports, - onNavigateToProduction + onNavigateToProduction, + onNavigateToInventory, + onNavigateToRecipes, + onNavigateToSales }) => { const { weather, + tenantId, isLoading, error, reload, @@ -31,13 +38,13 @@ const DashboardPage: React.FC = ({ metrics } = useDashboard(); - // Use real API data for order suggestions + // Use real API data for order suggestions - pass tenantId from dashboard const { dailyOrders: realDailyOrders, weeklyOrders: realWeeklyOrders, isLoading: ordersLoading, error: ordersError - } = useOrderSuggestions(); + } = useOrderSuggestions(tenantId); // Debug order suggestions console.log('๐Ÿ“ˆ Dashboard: OrderSuggestions data:', {