diff --git a/frontend/src/api/hooks/inventory.ts b/frontend/src/api/hooks/inventory.ts index 2a7cca26..d237e057 100644 --- a/frontend/src/api/hooks/inventory.ts +++ b/frontend/src/api/hooks/inventory.ts @@ -61,9 +61,9 @@ export const inventoryKeys = { export const useIngredients = ( tenantId: string, filter?: InventoryFilter, - options?: Omit, ApiError>, 'queryKey' | 'queryFn'> + options?: Omit, 'queryKey' | 'queryFn'> ) => { - return useQuery, ApiError>({ + return useQuery({ queryKey: inventoryKeys.ingredients.list(tenantId, filter), queryFn: () => inventoryService.getIngredients(tenantId, filter), enabled: !!tenantId, diff --git a/frontend/src/api/services/inventory.ts b/frontend/src/api/services/inventory.ts index 5266067e..3ffd6025 100644 --- a/frontend/src/api/services/inventory.ts +++ b/frontend/src/api/services/inventory.ts @@ -36,7 +36,7 @@ export class InventoryService { async getIngredients( tenantId: string, filter?: InventoryFilter - ): Promise> { + ): Promise { const queryParams = new URLSearchParams(); if (filter?.category) queryParams.append('category', filter.category); @@ -60,7 +60,7 @@ export class InventoryService { ? `${this.baseUrl}/${tenantId}/ingredients?${queryParams.toString()}` : `${this.baseUrl}/${tenantId}/ingredients`; - return apiClient.get>(url); + return apiClient.get(url); } async updateIngredient( @@ -218,8 +218,8 @@ export class InventoryService { if (endDate) queryParams.append('end_date', endDate); const url = queryParams.toString() - ? `${this.baseUrl}/${tenantId}/dashboard/analytics?${queryParams.toString()}` - : `${this.baseUrl}/${tenantId}/dashboard/analytics`; + ? `/tenants/${tenantId}/dashboard/analytics?${queryParams.toString()}` + : `/tenants/${tenantId}/dashboard/analytics`; return apiClient.get(url); } diff --git a/frontend/src/pages/app/operations/inventory/InventoryPage.tsx b/frontend/src/pages/app/operations/inventory/InventoryPage.tsx index cb90734e..0955ffa5 100644 --- a/frontend/src/pages/app/operations/inventory/InventoryPage.tsx +++ b/frontend/src/pages/app/operations/inventory/InventoryPage.tsx @@ -1,11 +1,11 @@ import React, { useState, useMemo } from 'react'; import { Plus, Download, AlertTriangle, Package, Clock, CheckCircle, Eye, Edit, Calendar, DollarSign } from 'lucide-react'; -import { Button, Input, Card, Badge, StatsGrid, StatusCard, getStatusColor, StatusModal } from '../../../../components/ui'; +import { Button, Input, Card, StatsGrid, StatusCard, getStatusColor, StatusModal } from '../../../../components/ui'; import { LoadingSpinner } from '../../../../components/shared'; import { formatters } from '../../../../components/ui/Stats/StatsPresets'; import { PageHeader } from '../../../../components/layout'; -import { InventoryForm, LowStockAlert } from '../../../../components/domain/inventory'; -import { useIngredients, useLowStockIngredients, useStockAnalytics } from '../../../../api/hooks/inventory'; +import { LowStockAlert } from '../../../../components/domain/inventory'; +import { useIngredients, useStockAnalytics } from '../../../../api/hooks/inventory'; import { useCurrentTenant } from '../../../../stores/tenant.store'; import { IngredientResponse } from '../../../../api/types/inventory'; @@ -25,21 +25,16 @@ const InventoryPage: React.FC = () => { error: ingredientsError } = useIngredients(tenantId, { search: searchTerm || undefined }); - const { - data: lowStockData, - isLoading: lowStockLoading - } = useLowStockIngredients(tenantId); - const { data: analyticsData, isLoading: analyticsLoading } = useStockAnalytics(tenantId); - const ingredients = ingredientsData?.items || []; - const lowStockItems = lowStockData || []; + const ingredients = ingredientsData || []; + const lowStockItems = ingredients.filter(ingredient => ingredient.stock_status === 'low_stock'); const getInventoryStatusConfig = (ingredient: IngredientResponse) => { - const { current_stock_level, low_stock_threshold, stock_status } = ingredient; + const { stock_status } = ingredient; switch (stock_status) { case 'out_of_stock': diff --git a/gateway/app/routes/tenant.py b/gateway/app/routes/tenant.py index 36b81d3f..d463785f 100644 --- a/gateway/app/routes/tenant.py +++ b/gateway/app/routes/tenant.py @@ -172,6 +172,13 @@ async def proxy_tenant_stock(request: Request, tenant_id: str = Path(...), path: target_path = f"/api/v1/tenants/{tenant_id}/stock/{path}".rstrip("/") return await _proxy_to_inventory_service(request, target_path, tenant_id=tenant_id) +@router.api_route("/{tenant_id}/dashboard/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"]) +async def proxy_tenant_dashboard(request: Request, tenant_id: str = Path(...), path: str = ""): + """Proxy tenant dashboard requests to inventory service""" + # The inventory service dashboard endpoints are tenant-scoped: /api/v1/tenants/{tenant_id}/dashboard/{path} + target_path = f"/api/v1/tenants/{tenant_id}/dashboard/{path}".rstrip("/") + return await _proxy_to_inventory_service(request, target_path, tenant_id=tenant_id) + # ================================================================ # TENANT-SCOPED PRODUCTION SERVICE ENDPOINTS # ================================================================ diff --git a/services/inventory/app/api/dashboard.py b/services/inventory/app/api/dashboard.py index 2b2a36d6..177990de 100644 --- a/services/inventory/app/api/dashboard.py +++ b/services/inventory/app/api/dashboard.py @@ -31,7 +31,7 @@ from app.schemas.dashboard import ( logger = structlog.get_logger() -router = APIRouter(prefix="/dashboard", tags=["dashboard"]) +router = APIRouter(tags=["dashboard"]) # ===== Dependency Injection ===== @@ -46,7 +46,7 @@ async def get_dashboard_service(db: AsyncSession = Depends(get_db)) -> Dashboard # ===== Main Dashboard Endpoints ===== -@router.get("/tenants/{tenant_id}/summary", response_model=InventoryDashboardSummary) +@router.get("/tenants/{tenant_id}/dashboard/summary", response_model=InventoryDashboardSummary) async def get_inventory_dashboard_summary( tenant_id: UUID = Path(...), filters: Optional[DashboardFilter] = None, @@ -74,7 +74,7 @@ async def get_inventory_dashboard_summary( ) -@router.get("/tenants/{tenant_id}/food-safety", response_model=FoodSafetyDashboard) +@router.get("/tenants/{tenant_id}/dashboard/food-safety", response_model=FoodSafetyDashboard) async def get_food_safety_dashboard( tenant_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), @@ -101,7 +101,7 @@ async def get_food_safety_dashboard( ) -@router.get("/tenants/{tenant_id}/analytics", response_model=InventoryAnalytics) +@router.get("/tenants/{tenant_id}/dashboard/analytics", response_model=InventoryAnalytics) async def get_inventory_analytics( tenant_id: UUID = Path(...), days_back: int = Query(30, ge=1, le=365, description="Number of days to analyze"), @@ -129,7 +129,7 @@ async def get_inventory_analytics( ) -@router.get("/tenants/{tenant_id}/business-model", response_model=BusinessModelInsights) +@router.get("/tenants/{tenant_id}/dashboard/business-model", response_model=BusinessModelInsights) async def get_business_model_insights( tenant_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), @@ -158,7 +158,7 @@ async def get_business_model_insights( # ===== Detailed Dashboard Data Endpoints ===== -@router.get("/tenants/{tenant_id}/stock-status", response_model=List[StockStatusSummary]) +@router.get("/tenants/{tenant_id}/dashboard/stock-status", response_model=List[StockStatusSummary]) async def get_stock_status_by_category( tenant_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), @@ -181,7 +181,7 @@ async def get_stock_status_by_category( ) -@router.get("/tenants/{tenant_id}/alerts-summary", response_model=List[AlertSummary]) +@router.get("/tenants/{tenant_id}/dashboard/alerts-summary", response_model=List[AlertSummary]) async def get_alerts_summary( tenant_id: UUID = Path(...), filters: Optional[AlertsFilter] = None, @@ -205,7 +205,7 @@ async def get_alerts_summary( ) -@router.get("/tenants/{tenant_id}/recent-activity", response_model=List[RecentActivity]) +@router.get("/tenants/{tenant_id}/dashboard/recent-activity", response_model=List[RecentActivity]) async def get_recent_activity( tenant_id: UUID = Path(...), limit: int = Query(20, ge=1, le=100, description="Number of activities to return"), @@ -234,7 +234,7 @@ async def get_recent_activity( # ===== Real-time Data Endpoints ===== -@router.get("/tenants/{tenant_id}/live-metrics") +@router.get("/tenants/{tenant_id}/dashboard/live-metrics") async def get_live_metrics( tenant_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), @@ -261,7 +261,7 @@ async def get_live_metrics( ) -@router.get("/tenants/{tenant_id}/temperature-status") +@router.get("/tenants/{tenant_id}/dashboard/temperature-status") async def get_temperature_monitoring_status( tenant_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep), @@ -289,7 +289,7 @@ async def get_temperature_monitoring_status( # ===== Dashboard Configuration Endpoints ===== -@router.get("/tenants/{tenant_id}/config") +@router.get("/tenants/{tenant_id}/dashboard/config") async def get_dashboard_config( tenant_id: UUID = Path(...), current_user: dict = Depends(get_current_user_dep) diff --git a/services/inventory/app/api/stock.py b/services/inventory/app/api/stock.py index c0736d1c..55b79b18 100644 --- a/services/inventory/app/api/stock.py +++ b/services/inventory/app/api/stock.py @@ -209,9 +209,9 @@ async def get_stock_entry( @router.put("/tenants/{tenant_id}/stock/{stock_id}", response_model=StockResponse) async def update_stock( + stock_data: StockUpdate, stock_id: UUID = Path(..., description="Stock entry ID"), tenant_id: UUID = Path(..., description="Tenant ID"), - stock_data: StockUpdate, db: AsyncSession = Depends(get_db) ): """Update stock entry""" @@ -269,8 +269,8 @@ async def delete_stock( @router.post("/tenants/{tenant_id}/stock/movements", response_model=StockMovementResponse) async def create_stock_movement( - tenant_id: UUID = Path(..., description="Tenant ID"), movement_data: StockMovementCreate, + tenant_id: UUID = Path(..., description="Tenant ID"), current_user: dict = Depends(get_current_user_dep), db: AsyncSession = Depends(get_db) ):