/** * Example usage of the restructured recipes API * Demonstrates tenant-dependent routing and React Query hooks */ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useRecipes, useRecipe, useCreateRecipe, useUpdateRecipe, useDeleteRecipe, useDuplicateRecipe, useActivateRecipe, useRecipeStatistics, useRecipeCategories, useRecipeFeasibility, type RecipeResponse, type RecipeCreate, type RecipeSearchParams, MeasurementUnit, } from '../api'; import { useCurrentTenant } from '../stores/tenant.store'; /** * Example: Recipe List Component * Shows how to use the tenant-dependent useRecipes hook */ export const RecipesList: React.FC = () => { const { t } = useTranslation('recipes'); const currentTenant = useCurrentTenant(); const [filters, setFilters] = useState({ limit: 20, offset: 0, }); // Use tenant-dependent recipes hook const { data: recipes, isLoading, error, refetch, } = useRecipes(currentTenant?.id || '', filters, { enabled: !!currentTenant?.id, }); if (!currentTenant) { return
{t('messages.no_tenant_selected')}
; } if (isLoading) { return
{t('messages.loading_recipes')}
; } if (error) { return
Error: {error.message}
; } return (

{t('title')}

{/* Search and filters */}
setFilters({ ...filters, search_term: e.target.value })} />
{/* Recipe cards */}
{recipes?.map((recipe) => ( ))}
{(!recipes || recipes.length === 0) && (
{t('messages.no_recipes_found')}
)}
); }; /** * Example: Individual Recipe Card */ interface RecipeCardProps { recipe: RecipeResponse; } const RecipeCard: React.FC = ({ recipe }) => { const { t } = useTranslation('recipes'); const currentTenant = useCurrentTenant(); // Mutation hooks for recipe actions const duplicateRecipe = useDuplicateRecipe(currentTenant?.id || ''); const activateRecipe = useActivateRecipe(currentTenant?.id || ''); const deleteRecipe = useDeleteRecipe(currentTenant?.id || ''); const handleDuplicate = () => { if (!currentTenant) return; duplicateRecipe.mutate({ id: recipe.id, data: { new_name: `${recipe.name} (Copy)` } }, { onSuccess: () => { alert(t('messages.recipe_duplicated')); }, onError: (error) => { alert(error.message); } }); }; const handleActivate = () => { if (!currentTenant) return; activateRecipe.mutate(recipe.id, { onSuccess: () => { alert(t('messages.recipe_activated')); } }); }; const handleDelete = () => { if (!currentTenant) return; if (confirm(t('messages.confirm_delete'))) { deleteRecipe.mutate(recipe.id, { onSuccess: () => { alert(t('messages.recipe_deleted')); } }); } }; return (

{recipe.name}

{recipe.description}

{t(`status.${recipe.status}`)} {recipe.category} {t(`difficulty.${recipe.difficulty_level}`)}
{recipe.status === 'draft' && ( )}
); }; /** * Example: Recipe Detail View */ interface RecipeDetailProps { recipeId: string; } export const RecipeDetail: React.FC = ({ recipeId }) => { const { t } = useTranslation('recipes'); const currentTenant = useCurrentTenant(); // Get individual recipe with tenant context const { data: recipe, isLoading, error, } = useRecipe(currentTenant?.id || '', recipeId, { enabled: !!(currentTenant?.id && recipeId), }); // Check feasibility const { data: feasibility, } = useRecipeFeasibility( currentTenant?.id || '', recipeId, 1.0, // batch multiplier { enabled: !!(currentTenant?.id && recipeId), } ); if (!currentTenant) { return
{t('messages.no_tenant_selected')}
; } if (isLoading) { return
{t('messages.loading_recipe')}
; } if (error || !recipe) { return
Recipe not found
; } return (

{recipe.name}

{recipe.description}

Yield: {recipe.yield_quantity} {recipe.yield_unit} Prep: {recipe.prep_time_minutes}min Cook: {recipe.cook_time_minutes}min Total: {recipe.total_time_minutes}min
{/* Feasibility check */} {feasibility && (

{t('feasibility.title')}

{feasibility.feasible ? t('feasibility.feasible') : t('feasibility.not_feasible') }

{feasibility.missing_ingredients.length > 0 && (

{t('feasibility.missing_ingredients')}

    {feasibility.missing_ingredients.map((ingredient, index) => (
  • {JSON.stringify(ingredient)}
  • ))}
)}
)} {/* Ingredients */}

{t('ingredients.title')}

    {recipe.ingredients?.map((ingredient) => (
  • {ingredient.quantity} {ingredient.unit} - {ingredient.ingredient_id} {ingredient.preparation_method && ( ({ingredient.preparation_method}) )} {ingredient.is_optional && ( ({t('ingredients.is_optional')}) )}
  • ))}
{/* Instructions */} {recipe.instructions && (

{t('fields.instructions')}

{JSON.stringify(recipe.instructions, null, 2)}
)}
); }; /** * Example: Recipe Creation Form */ export const CreateRecipeForm: React.FC = () => { const { t } = useTranslation('recipes'); const currentTenant = useCurrentTenant(); const [formData, setFormData] = useState({ name: '', finished_product_id: '', yield_quantity: 1, yield_unit: MeasurementUnit.UNITS, difficulty_level: 1, batch_size_multiplier: 1.0, is_seasonal: false, is_signature_item: false, ingredients: [], }); const createRecipe = useCreateRecipe(currentTenant?.id || '', { onSuccess: (data) => { alert(t('messages.recipe_created')); // Reset form or redirect setFormData({ name: '', finished_product_id: '', yield_quantity: 1, yield_unit: MeasurementUnit.UNITS, difficulty_level: 1, batch_size_multiplier: 1.0, is_seasonal: false, is_signature_item: false, ingredients: [], }); }, onError: (error) => { alert(error.message); } }); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!currentTenant) { alert('No tenant selected'); return; } if (!formData.name.trim()) { alert(t('messages.recipe_name_required')); return; } if (formData.ingredients.length === 0) { alert(t('messages.at_least_one_ingredient')); return; } createRecipe.mutate(formData); }; if (!currentTenant) { return
{t('messages.no_tenant_selected')}
; } return (

{t('actions.create_recipe')}

setFormData({ ...formData, name: e.target.value })} placeholder={t('placeholders.recipe_name')} required />