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:', {