Esta es una aplicación web interactiva desarrollada con React que permite a los usuarios generar recetas personalizadas utilizando inteligencia artificial. Los usuarios pueden ingresar una lista de ingredientes disponibles y obtener sugerencias de recetas creativas basadas en esos ingredientes, utilizando el modelo de IA Mistral a través de la plataforma HuggingFace.
- 🍎 Gestión de Ingredientes: Agrega fácilmente ingredientes a tu lista con un formulario intuitivo
- 🤖 Generación de Recetas con IA: Utiliza el modelo Mistral para crear recetas originales basadas en tus ingredientes
- 📱 Interfaz Responsiva: Diseño moderno y adaptable a diferentes dispositivos
- ⚡ Rendimiento Óptimo: Construida con Vite para una experiencia de desarrollo rápida
- 🎨 Renderizado Markdown: Presentación elegante de recetas con formato enriquecido
- 🔄 Estado Reactivo: Actualización en tiempo real de la interfaz basada en el estado de la aplicación
Vista previa de la aplicación Chef Claude mostrando la interfaz principal con lista de ingredientes y receta generada.
Para ver la demo en vivo, accede directamente al proyecto desplegado.
- Node.js (versión 16 o superior)
- npm o yarn
- Token de acceso de HuggingFace (para la funcionalidad de IA)
# Clona el repositorio
git clone https://github.com/Seb-RM/React-State-Recipe_App.git
cd React-State-Recipe_App
# Instala las dependencias
npm install
# Configura las variables de entorno
# Crea un archivo .env en la raíz del proyecto
echo "VITE_HF_ACCESS_TOKEN=tu_token_de_huggingface" > .env- Token de HuggingFace: Obtén un token gratuito en HuggingFace
- Variables de Entorno: Asegúrate de que
VITE_HF_ACCESS_TOKENesté configurado en tu archivo.env
- Agregar Ingredientes: Escribe un ingrediente en el campo de texto y haz clic en "Add ingredient"
- Generar Receta: Una vez que tengas al menos 4 ingredientes, aparecerá un botón para obtener una receta
- Ver Resultado: La receta se mostrará automáticamente con formato markdown
- Cocina con Restos: Ingresa ingredientes que tengas disponibles y descubre nuevas combinaciones
- Exploración Culinaria: Experimenta con ingredientes inusuales para recetas creativas
- Planificación de Menús: Genera ideas para comidas basadas en inventario
La aplicación no requiere configuración adicional, pero puedes modificar el prompt del sistema en src/assets/ai.js para personalizar el comportamiento de la IA.
Propósito: Componente raíz de la aplicación que estructura el layout principal.
Conceptos Clave: Componentes funcionales, composición de componentes, estructura de aplicación React.
Fragmentos Destacados:
import Header from "./components/Header";
import Main from "./components/Main";
export default function App() {
return (
<>
<Header/>
<Main/>
</>
);
}Flujo de Datos: Renderiza Header y Main como componentes hijos. No maneja estado propio, delegando la lógica a Main.
Propósito: Componente de encabezado que muestra el logo y título de la aplicación.
Conceptos Clave: Importación de assets, JSX, estructura semántica HTML.
Fragmentos Destacados:
import chefClaudeLogo from "../assets/images/chef-claude-icon.png";
export default function Header() {
return (
<header>
<img src={chefClaudeLogo} />
<h1>Chef Claude</h1>
</header>
);
}Flujo de Datos: Componente estático sin props ni estado. Renderiza elementos fijos.
Propósito: Componente principal que maneja el estado global de la aplicación y coordina la lógica de usuario.
Conceptos Clave: Hooks de React (useState, useEffect), manejo de formularios, refs, async/await, gestión de estado.
Fragmentos Destacados:
const [ingredients, setIngredients] = React.useState([]);
const [recipe, setRecipe] = React.useState("");
async function getRecipe() {
const recipeMarkdown = await getRecipeFromMistral(ingredients);
setRecipe(recipeMarkdown);
}
function addIngredient(formData) {
const newIngredient = formData.get("ingredient");
setIngredients((prevIngredients) => [...prevIngredients, newIngredient]);
}Flujo de Datos:
- Entrada: Formulario → addIngredient → actualiza estado ingredients
- Procesamiento: getRecipe → llama a API → actualiza estado recipe
- Salida: Renderiza IngredientsList y ClaudeRecipe basados en estado
API/DOM: Utiliza refs para scroll automático, maneja eventos de formulario con action functions (React 19), actualiza DOM condicionalmente basado en estado.
Propósito: Componente que muestra la lista de ingredientes agregados y el botón para generar receta.
Conceptos Clave: Mapeo de arrays, renderizado condicional, props, accesibilidad (aria-live).
Fragmentos Destacados:
const ingredientsListItems = props.ingredients.map((ingredient) => (
<li key={ingredient}>{ingredient}</li>
));
{props.ingredients.length > 3 && (
<div className="get-recipe-container">
<button onClick={props.getRecipe}>Get a recipe</button>
</div>
)}Flujo de Datos: Recibe props (ingredients, getRecipe, ref) y renderiza lista. El botón aparece condicionalmente.
API/DOM: Utiliza aria-live para accesibilidad, maneja eventos onClick.
Propósito: Componente que renderiza la receta generada utilizando ReactMarkdown.
Conceptos Clave: Renderizado de markdown, props, accesibilidad.
Fragmentos Destacados:
import ReactMarkdown from "react-markdown";
export default function ClaudeRecipe(props) {
return (
<section className="suggested-recipe-container" aria-live="polite">
<h2>Chef Claude Recommends:</h2>
<ReactMarkdown>{props.recipe}</ReactMarkdown>
</section>
);
}Flujo de Datos: Recibe prop recipe (string markdown) y la renderiza como HTML.
API/DOM: Utiliza ReactMarkdown para conversión de markdown a JSX, aria-live para anuncios de cambios.
Propósito: Módulo que maneja la integración con la API de HuggingFace para generación de recetas.
Conceptos Clave: API calls asíncronas, manejo de errores, prompts de sistema, configuración de modelos de IA.
Fragmentos Destacados:
const SYSTEM_PROMPT = `
You are an assistant that receives a list of ingredients that a user has and suggests a recipe they could make with some or all of those ingredients...
`;
export async function getRecipeFromMistral(ingredientsArr) {
const ingredientsString = ingredientsArr.join(", ");
const response = await hf.chatCompletion({
model: "mistralai/Mistral-7B-Instruct-v0.2",
messages: [
{ role: "system", content: SYSTEM_PROMPT },
{ role: "user", content: \`I have \${ingredientsString}. Please give me a recipe you'd recommend I make!\` },
],
max_tokens: 1024,
});
return response.choices[0].message.content;
}Flujo de Datos: Entrada (array de ingredientes) → conversión a string → API call → respuesta markdown.
API/DOM: Maneja promesas, errores de red, configuración de tokens de autenticación.
Propósito: Estilos globales y específicos para componentes de la aplicación.
Conceptos Clave: CSS moderno, diseño responsivo, variables implícitas, box-sizing, sombras, transiciones.
Fragmentos Destacados:
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Inter, sans-serif;
background-color: #FAFAF8;
}
.add-ingredient-form {
display: flex;
justify-content: center;
gap: 12px;
height: 38px;
}Flujo de Datos: Estilos aplicados globalmente afectan el renderizado de todos los componentes.
La aplicación sigue una arquitectura de componentes React funcional con hooks, organizada de la siguiente manera:
App
├── Header (estático)
└── Main (estado global)
├── Formulario (agregar ingredientes)
├── IngredientsList (lista + botón condicional)
└── ClaudeRecipe (renderizado markdown)
Patrones de Diseño Implementados:
- Componentes Funcionales: Uso de hooks para estado y efectos
- Props Down: Flujo de datos unidireccional desde Main hacia componentes hijos
- Separación de Concerns: Lógica de IA separada en módulo ai.js
- Renderizado Condicional: UI que cambia basado en estado (cantidad de ingredientes, presencia de receta)
Gestión de Estado: Estado centralizado en Main.jsx con useState para ingredients y recipe. useEffect para scroll automático.
Gestión de Dependencias:
- React para UI
- HuggingFace Inference para IA
- ReactMarkdown para renderizado
- Vite para desarrollo y construcción
Esta arquitectura permite una fácil mantenibilidad, escalabilidad y testing de componentes individuales.
