Add frontend imporvements 2

This commit is contained in:
Urtzi Alfaro
2025-09-09 22:27:52 +02:00
parent 2a05048912
commit aff644d793
6 changed files with 32 additions and 30 deletions

View File

@@ -61,9 +61,9 @@ export const inventoryKeys = {
export const useIngredients = (
tenantId: string,
filter?: InventoryFilter,
options?: Omit<UseQueryOptions<PaginatedResponse<IngredientResponse>, ApiError>, 'queryKey' | 'queryFn'>
options?: Omit<UseQueryOptions<IngredientResponse[], ApiError>, 'queryKey' | 'queryFn'>
) => {
return useQuery<PaginatedResponse<IngredientResponse>, ApiError>({
return useQuery<IngredientResponse[], ApiError>({
queryKey: inventoryKeys.ingredients.list(tenantId, filter),
queryFn: () => inventoryService.getIngredients(tenantId, filter),
enabled: !!tenantId,

View File

@@ -36,7 +36,7 @@ export class InventoryService {
async getIngredients(
tenantId: string,
filter?: InventoryFilter
): Promise<PaginatedResponse<IngredientResponse>> {
): Promise<IngredientResponse[]> {
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<PaginatedResponse<IngredientResponse>>(url);
return apiClient.get<IngredientResponse[]>(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);
}

View File

@@ -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':

View File

@@ -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
# ================================================================

View File

@@ -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)

View File

@@ -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)
):