Create the frontend receipes page to use real API

This commit is contained in:
Urtzi Alfaro
2025-09-19 21:39:04 +02:00
parent 8002d89d2b
commit d18c64ce6e
36 changed files with 3356 additions and 3171 deletions

View File

@@ -4,7 +4,7 @@ API endpoints for recipe management
"""
from fastapi import APIRouter, Depends, HTTPException, Header, Query
from sqlalchemy.orm import Session
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List, Optional
from uuid import UUID
import logging
@@ -25,12 +25,6 @@ logger = logging.getLogger(__name__)
router = APIRouter()
def get_tenant_id(x_tenant_id: str = Header(...)) -> UUID:
"""Extract tenant ID from header"""
try:
return UUID(x_tenant_id)
except ValueError:
raise HTTPException(status_code=400, detail="Invalid tenant ID format")
def get_user_id(x_user_id: str = Header(...)) -> UUID:
@@ -41,12 +35,12 @@ def get_user_id(x_user_id: str = Header(...)) -> UUID:
raise HTTPException(status_code=400, detail="Invalid user ID format")
@router.post("/", response_model=RecipeResponse)
@router.post("/{tenant_id}/recipes", response_model=RecipeResponse)
async def create_recipe(
tenant_id: UUID,
recipe_data: RecipeCreate,
tenant_id: UUID = Depends(get_tenant_id),
user_id: UUID = Depends(get_user_id),
db: Session = Depends(get_db)
db: AsyncSession = Depends(get_db)
):
"""Create a new recipe"""
try:
@@ -76,16 +70,16 @@ async def create_recipe(
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/{recipe_id}", response_model=RecipeResponse)
@router.get("/{tenant_id}/recipes/{recipe_id}", response_model=RecipeResponse)
async def get_recipe(
tenant_id: UUID,
recipe_id: UUID,
tenant_id: UUID = Depends(get_tenant_id),
db: Session = Depends(get_db)
db: AsyncSession = Depends(get_db)
):
"""Get recipe by ID with ingredients"""
try:
recipe_service = RecipeService(db)
recipe = recipe_service.get_recipe_with_ingredients(recipe_id)
recipe = await recipe_service.get_recipe_with_ingredients(recipe_id)
if not recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
@@ -103,20 +97,20 @@ async def get_recipe(
raise HTTPException(status_code=500, detail="Internal server error")
@router.put("/{recipe_id}", response_model=RecipeResponse)
@router.put("/{tenant_id}/recipes/{recipe_id}", response_model=RecipeResponse)
async def update_recipe(
tenant_id: UUID,
recipe_id: UUID,
recipe_data: RecipeUpdate,
tenant_id: UUID = Depends(get_tenant_id),
user_id: UUID = Depends(get_user_id),
db: Session = Depends(get_db)
db: AsyncSession = Depends(get_db)
):
"""Update an existing recipe"""
try:
recipe_service = RecipeService(db)
# Check if recipe exists and belongs to tenant
existing_recipe = recipe_service.get_recipe_with_ingredients(recipe_id)
existing_recipe = await recipe_service.get_recipe_with_ingredients(recipe_id)
if not existing_recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
@@ -149,26 +143,26 @@ async def update_recipe(
raise HTTPException(status_code=500, detail="Internal server error")
@router.delete("/{recipe_id}")
@router.delete("/{tenant_id}/recipes/{recipe_id}")
async def delete_recipe(
tenant_id: UUID,
recipe_id: UUID,
tenant_id: UUID = Depends(get_tenant_id),
db: Session = Depends(get_db)
db: AsyncSession = Depends(get_db)
):
"""Delete a recipe"""
try:
recipe_service = RecipeService(db)
# Check if recipe exists and belongs to tenant
existing_recipe = recipe_service.get_recipe_with_ingredients(recipe_id)
existing_recipe = await recipe_service.get_recipe_with_ingredients(recipe_id)
if not existing_recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
if existing_recipe["tenant_id"] != str(tenant_id):
raise HTTPException(status_code=403, detail="Access denied")
# Use repository to delete
success = recipe_service.recipe_repo.delete(recipe_id)
# Use service to delete
success = await recipe_service.delete_recipe(recipe_id)
if not success:
raise HTTPException(status_code=404, detail="Recipe not found")
@@ -181,9 +175,9 @@ async def delete_recipe(
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/", response_model=List[RecipeResponse])
@router.get("/{tenant_id}/recipes", response_model=List[RecipeResponse])
async def search_recipes(
tenant_id: UUID = Depends(get_tenant_id),
tenant_id: UUID,
search_term: Optional[str] = Query(None),
status: Optional[str] = Query(None),
category: Optional[str] = Query(None),
@@ -192,13 +186,13 @@ async def search_recipes(
difficulty_level: Optional[int] = Query(None, ge=1, le=5),
limit: int = Query(100, ge=1, le=1000),
offset: int = Query(0, ge=0),
db: Session = Depends(get_db)
db: AsyncSession = Depends(get_db)
):
"""Search recipes with filters"""
try:
recipe_service = RecipeService(db)
recipes = recipe_service.search_recipes(
recipes = await recipe_service.search_recipes(
tenant_id=tenant_id,
search_term=search_term,
status=status,
@@ -217,13 +211,13 @@ async def search_recipes(
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/{recipe_id}/duplicate", response_model=RecipeResponse)
@router.post("/{tenant_id}/recipes/{recipe_id}/duplicate", response_model=RecipeResponse)
async def duplicate_recipe(
tenant_id: UUID,
recipe_id: UUID,
duplicate_data: RecipeDuplicateRequest,
tenant_id: UUID = Depends(get_tenant_id),
user_id: UUID = Depends(get_user_id),
db: Session = Depends(get_db)
db: AsyncSession = Depends(get_db)
):
"""Create a duplicate of an existing recipe"""
try:
@@ -255,19 +249,19 @@ async def duplicate_recipe(
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/{recipe_id}/activate", response_model=RecipeResponse)
@router.post("/{tenant_id}/recipes/{recipe_id}/activate", response_model=RecipeResponse)
async def activate_recipe(
tenant_id: UUID,
recipe_id: UUID,
tenant_id: UUID = Depends(get_tenant_id),
user_id: UUID = Depends(get_user_id),
db: Session = Depends(get_db)
db: AsyncSession = Depends(get_db)
):
"""Activate a recipe for production"""
try:
recipe_service = RecipeService(db)
# Check if recipe exists and belongs to tenant
existing_recipe = recipe_service.get_recipe_with_ingredients(recipe_id)
existing_recipe = await recipe_service.get_recipe_with_ingredients(recipe_id)
if not existing_recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
@@ -288,19 +282,19 @@ async def activate_recipe(
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/{recipe_id}/feasibility", response_model=RecipeFeasibilityResponse)
@router.get("/{tenant_id}/recipes/{recipe_id}/feasibility", response_model=RecipeFeasibilityResponse)
async def check_recipe_feasibility(
tenant_id: UUID,
recipe_id: UUID,
batch_multiplier: float = Query(1.0, gt=0),
tenant_id: UUID = Depends(get_tenant_id),
db: Session = Depends(get_db)
db: AsyncSession = Depends(get_db)
):
"""Check if recipe can be produced with current inventory"""
try:
recipe_service = RecipeService(db)
# Check if recipe exists and belongs to tenant
existing_recipe = recipe_service.get_recipe_with_ingredients(recipe_id)
existing_recipe = await recipe_service.get_recipe_with_ingredients(recipe_id)
if not existing_recipe:
raise HTTPException(status_code=404, detail="Recipe not found")
@@ -321,15 +315,15 @@ async def check_recipe_feasibility(
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/statistics/dashboard", response_model=RecipeStatisticsResponse)
@router.get("/{tenant_id}/recipes/statistics/dashboard", response_model=RecipeStatisticsResponse)
async def get_recipe_statistics(
tenant_id: UUID = Depends(get_tenant_id),
db: Session = Depends(get_db)
tenant_id: UUID,
db: AsyncSession = Depends(get_db)
):
"""Get recipe statistics for dashboard"""
try:
recipe_service = RecipeService(db)
stats = recipe_service.get_recipe_statistics(tenant_id)
stats = await recipe_service.get_recipe_statistics(tenant_id)
return RecipeStatisticsResponse(**stats)
@@ -338,17 +332,17 @@ async def get_recipe_statistics(
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/categories/list")
@router.get("/{tenant_id}/recipes/categories/list")
async def get_recipe_categories(
tenant_id: UUID = Depends(get_tenant_id),
db: Session = Depends(get_db)
tenant_id: UUID,
db: AsyncSession = Depends(get_db)
):
"""Get list of recipe categories used by tenant"""
try:
recipe_service = RecipeService(db)
# Get categories from existing recipes
recipes = recipe_service.search_recipes(tenant_id, limit=1000)
recipes = await recipe_service.search_recipes(tenant_id, limit=1000)
categories = list(set(recipe["category"] for recipe in recipes if recipe["category"]))
categories.sort()