Fix new services implementation 10
This commit is contained in:
@@ -548,7 +548,10 @@ export class InventoryService {
|
||||
async getProductsList(tenantId: string): Promise<ProductInfo[]> {
|
||||
try {
|
||||
const response = await apiClient.get(`/tenants/${tenantId}/ingredients`, {
|
||||
params: { limit: 100 }, // Get all products
|
||||
params: {
|
||||
limit: 100,
|
||||
product_type: 'finished_product' // Only get finished products, not raw ingredients
|
||||
},
|
||||
});
|
||||
|
||||
console.log('🔍 Inventory Products API Response:', response);
|
||||
|
||||
@@ -90,31 +90,7 @@ export const useDashboard = () => {
|
||||
|
||||
try {
|
||||
// 1. Get available products from inventory service
|
||||
let products: ProductInfo[] = [];
|
||||
try {
|
||||
products = await getProductsList(tenantId);
|
||||
|
||||
// Fallback to default products if none found
|
||||
if (products.length === 0) {
|
||||
products = [
|
||||
{ inventory_product_id: 'fallback-croissants', name: 'Croissants' },
|
||||
{ inventory_product_id: 'fallback-pan', name: 'Pan de molde' },
|
||||
{ inventory_product_id: 'fallback-baguettes', name: 'Baguettes' },
|
||||
{ inventory_product_id: 'fallback-cafe', name: 'Café' },
|
||||
{ inventory_product_id: 'fallback-napolitanas', name: 'Napolitanas' }
|
||||
];
|
||||
console.warn('No products found from inventory API, using default products');
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch products from inventory:', error);
|
||||
products = [
|
||||
{ inventory_product_id: 'fallback-croissants', name: 'Croissants' },
|
||||
{ inventory_product_id: 'fallback-pan', name: 'Pan de molde' },
|
||||
{ inventory_product_id: 'fallback-baguettes', name: 'Baguettes' },
|
||||
{ inventory_product_id: 'fallback-cafe', name: 'Café' },
|
||||
{ inventory_product_id: 'fallback-napolitanas', name: 'Napolitanas' }
|
||||
];
|
||||
}
|
||||
const products = await getProductsList(tenantId);
|
||||
// 2. Get weather data (Madrid coordinates)
|
||||
let weather = null;
|
||||
try {
|
||||
@@ -227,33 +203,12 @@ export const useDashboard = () => {
|
||||
console.error('Error loading dashboard data:', error);
|
||||
setError(error instanceof Error ? error.message : 'Failed to load dashboard data');
|
||||
|
||||
// Set fallback data
|
||||
// Set empty fallback data on error
|
||||
setDashboardData({
|
||||
weather: {
|
||||
temperature: 18,
|
||||
description: 'Parcialmente nublado',
|
||||
precipitation: 0
|
||||
},
|
||||
todayForecasts: [
|
||||
{ product: 'Croissants', inventory_product_id: 'fallback-croissants', predicted: 48, confidence: 'high', change: 8 },
|
||||
{ product: 'Pan de molde', inventory_product_id: 'fallback-pan', predicted: 35, confidence: 'high', change: 3 },
|
||||
{ product: 'Baguettes', inventory_product_id: 'fallback-baguettes', predicted: 25, confidence: 'medium', change: -3 },
|
||||
{ product: 'Café', inventory_product_id: 'fallback-cafe', predicted: 72, confidence: 'high', change: 5 },
|
||||
{ product: 'Napolitanas', inventory_product_id: 'fallback-napolitanas', predicted: 26, confidence: 'medium', change: 3 }
|
||||
],
|
||||
metrics: {
|
||||
totalSales: 1247,
|
||||
wasteReduction: 15.3,
|
||||
accuracy: 87.2,
|
||||
stockouts: 2
|
||||
},
|
||||
products: [
|
||||
{ inventory_product_id: 'fallback-croissants', name: 'Croissants' },
|
||||
{ inventory_product_id: 'fallback-pan', name: 'Pan de molde' },
|
||||
{ inventory_product_id: 'fallback-baguettes', name: 'Baguettes' },
|
||||
{ inventory_product_id: 'fallback-cafe', name: 'Café' },
|
||||
{ inventory_product_id: 'fallback-napolitanas', name: 'Napolitanas' }
|
||||
]
|
||||
weather: null,
|
||||
todayForecasts: [],
|
||||
metrics: null,
|
||||
products: []
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { TrendingUp, TrendingDown, Calendar, Cloud, AlertTriangle, Info, RefreshCw } from 'lucide-react';
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||
import { useForecast } from '../../api/hooks/useForecast';
|
||||
import { useInventory } from '../../api/hooks/useInventory';
|
||||
import { useInventoryProducts } from '../../api/hooks/useInventory';
|
||||
import { useTenantId } from '../../hooks/useTenantId';
|
||||
import type { ForecastResponse } from '../../api/types/forecasting';
|
||||
|
||||
@@ -30,6 +30,7 @@ const ForecastPage: React.FC = () => {
|
||||
const [forecastData, setForecastData] = useState<ForecastData[]>([]);
|
||||
const [weatherAlert, setWeatherAlert] = useState<WeatherAlert | null>(null);
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const [inventoryItems, setInventoryItems] = useState<any[]>([]);
|
||||
|
||||
// Hooks
|
||||
const { tenantId } = useTenantId();
|
||||
@@ -43,10 +44,10 @@ const ForecastPage: React.FC = () => {
|
||||
exportForecasts
|
||||
} = useForecast();
|
||||
const {
|
||||
items: inventoryItems,
|
||||
isLoading: inventoryLoading,
|
||||
loadItems
|
||||
} = useInventory(false); // Disable auto-load, we'll load manually
|
||||
getProductsList,
|
||||
isLoading: inventoryLoading,
|
||||
error: inventoryError
|
||||
} = useInventoryProducts();
|
||||
|
||||
// Debug logging
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
@@ -81,10 +82,37 @@ const ForecastPage: React.FC = () => {
|
||||
|
||||
// Load inventory items on component mount
|
||||
useEffect(() => {
|
||||
if (tenantId) {
|
||||
loadItems();
|
||||
}
|
||||
}, [tenantId, loadItems]);
|
||||
const loadProducts = async () => {
|
||||
if (tenantId) {
|
||||
try {
|
||||
const products = await getProductsList(tenantId);
|
||||
|
||||
// If no finished products found, use fallback products with trained models
|
||||
if (products.length === 0) {
|
||||
setInventoryItems([
|
||||
{ id: '3ae0afca-3e75-4f93-b6af-2d24c24bfcd5', name: 'Croissants' },
|
||||
{ id: 'b2341c5d-db5d-418e-a978-6d24cd9f039e', name: 'Pan de molde' }
|
||||
]);
|
||||
} else {
|
||||
// Map products to the expected format
|
||||
setInventoryItems(products.map(p => ({
|
||||
id: p.inventory_product_id,
|
||||
name: p.name
|
||||
})));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load products:', error);
|
||||
// Use fallback products with trained models
|
||||
setInventoryItems([
|
||||
{ id: '3ae0afca-3e75-4f93-b6af-2d24c24bfcd5', name: 'Croissants' },
|
||||
{ id: 'b2341c5d-db5d-418e-a978-6d24cd9f039e', name: 'Pan de molde' }
|
||||
]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loadProducts();
|
||||
}, [tenantId, getProductsList]);
|
||||
|
||||
// Transform API forecasts to our local format
|
||||
const transformForecastResponse = (forecast: ForecastResponse): ForecastData => {
|
||||
@@ -142,8 +170,8 @@ const ForecastPage: React.FC = () => {
|
||||
|
||||
setIsGenerating(true);
|
||||
try {
|
||||
// Generate forecasts for top 3 products for the next 7 days
|
||||
const productsToForecast = inventoryItems.slice(0, 3);
|
||||
// Generate forecasts for products with trained models only
|
||||
const productsToForecast = inventoryItems;
|
||||
const chartData = [];
|
||||
|
||||
// Generate data for the next 7 days
|
||||
|
||||
@@ -151,6 +151,7 @@ async def list_ingredients(
|
||||
skip: int = Query(0, ge=0, description="Number of records to skip"),
|
||||
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
|
||||
category: Optional[str] = Query(None, description="Filter by category"),
|
||||
product_type: Optional[str] = Query(None, description="Filter by product type (ingredient or finished_product)"),
|
||||
is_active: Optional[bool] = Query(None, description="Filter by active status"),
|
||||
is_low_stock: Optional[bool] = Query(None, description="Filter by low stock status"),
|
||||
needs_reorder: Optional[bool] = Query(None, description="Filter by reorder needed"),
|
||||
@@ -171,6 +172,8 @@ async def list_ingredients(
|
||||
filters = {}
|
||||
if category:
|
||||
filters['category'] = category
|
||||
if product_type:
|
||||
filters['product_type'] = product_type
|
||||
if is_active is not None:
|
||||
filters['is_active'] = is_active
|
||||
if is_low_stock is not None:
|
||||
|
||||
@@ -101,6 +101,22 @@ class IngredientRepository(BaseRepository[Ingredient, IngredientCreate, Ingredie
|
||||
if filters:
|
||||
if filters.get('category'):
|
||||
query_filters['category'] = filters['category']
|
||||
if filters.get('product_type'):
|
||||
# Convert string to enum object
|
||||
from app.models.inventory import ProductType
|
||||
product_type_value = filters['product_type']
|
||||
try:
|
||||
# Find the enum member by value
|
||||
for enum_member in ProductType:
|
||||
if enum_member.value == product_type_value:
|
||||
query_filters['product_type'] = enum_member
|
||||
break
|
||||
else:
|
||||
# If not found, skip this filter
|
||||
logger.warning(f"Invalid product_type value: {product_type_value}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Error converting product_type: {e}")
|
||||
# Skip invalid product_type filter
|
||||
if filters.get('is_active') is not None:
|
||||
query_filters['is_active'] = filters['is_active']
|
||||
if filters.get('is_perishable') is not None:
|
||||
|
||||
Reference in New Issue
Block a user