import React, { useState } from ‘react’;
import { Plus, Trash2, Calculator, Download, Package } from ‘lucide-react’;
export default function JerkyCostingApp() {
const [view, setView] = useState(‘ingredients’);
const [ingredients, setIngredients] = useState([]);
const [recipes, setRecipes] = useState([]);
const [selectedRecipe, setSelectedRecipe] = useState(null);
// Ingredient form state
const [newIngredient, setNewIngredient] = useState({
name: ”,
weight: ”,
unit: ‘lb’,
costPerUnit: ”,
totalPaid: ”,
supplier: ”
});
// Recipe form state
const [newRecipe, setNewRecipe] = useState({
name: ”,
batchSize: ”,
items: []
});
const [recipeItem, setRecipeItem] = useState({
ingredientId: ”,
quantity: ”
});
// Add ingredient
const handleAddIngredient = () => {
if (newIngredient.name && newIngredient.weight && newIngredient.costPerUnit && newIngredient.totalPaid) {
const ingredient = {
id: Date.now(),
…newIngredient,
weight: parseFloat(newIngredient.weight),
costPerUnit: parseFloat(newIngredient.costPerUnit),
totalPaid: parseFloat(newIngredient.totalPaid)
};
setIngredients([…ingredients, ingredient]);
setNewIngredient({ name: ”, weight: ”, unit: ‘lb’, costPerUnit: ”, totalPaid: ”, supplier: ” });
}
};
// Add recipe item
const handleAddRecipeItem = () => {
if (recipeItem.ingredientId && recipeItem.quantity) {
setNewRecipe({
…newRecipe,
items: […newRecipe.items, {
ingredientId: recipeItem.ingredientId,
quantity: parseFloat(recipeItem.quantity)
}]
});
setRecipeItem({ ingredientId: ”, quantity: ” });
}
};
// Save recipe
const handleSaveRecipe = () => {
if (newRecipe.name && newRecipe.batchSize && newRecipe.items.length > 0) {
const recipe = {
id: Date.now(),
…newRecipe,
batchSize: parseFloat(newRecipe.batchSize)
};
setRecipes([…recipes, recipe]);
setNewRecipe({ name: ”, batchSize: ”, items: [] });
}
};
// Calculate recipe cost
const calculateRecipeCost = (recipe) => {
return recipe.items.reduce((total, item) => {
const ingredient = ingredients.find(ing => ing.id === item.ingredientId);
if (ingredient) {
return total + (ingredient.costPerUnit * item.quantity);
}
return total;
}, 0);
};
// Export to Notion
const handleExportToNotion = async (recipe) => {
const totalCost = calculateRecipeCost(recipe);
const costPerUnit = totalCost / recipe.batchSize;
const notionData = {
parent: { database_id: “YOUR_DATABASE_ID” },
properties: {
“Recipe Name”: { title: [{ text: { content: recipe.name } }] },
“Batch Size”: { number: recipe.batchSize },
“Total Cost”: { number: parseFloat(totalCost.toFixed(2)) },
“Cost Per Unit”: { number: parseFloat(costPerUnit.toFixed(2)) }
}
};
alert(‘To enable Notion export:\n1. Create a Notion integration at notion.so/my-integrations\n2. Create a database in Notion\n3. Share the database with your integration\n4. Replace YOUR_DATABASE_ID in the code with your database ID\n\nData to export:\n’ + JSON.stringify(notionData, null, 2));
};
return (
Beef Jerky Recipe Costing
Track ingredients, manage recipes, and calculate costs
{/* Navigation */}
{/* Ingredients View */}
{view === ‘ingredients’ && (
Add Ingredient
setNewIngredient({…newIngredient, name: e.target.value})}
className=”w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent”
placeholder=”e.g., Beef Eye Round”
/>
setNewIngredient({…newIngredient, weight: e.target.value})}
className=”w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent”
placeholder=”10″
/>
setNewIngredient({…newIngredient, costPerUnit: e.target.value})}
className=”w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent”
placeholder=”4.99″
/>
setNewIngredient({…newIngredient, totalPaid: e.target.value})}
className=”w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent”
placeholder=”49.90″
/>
setNewIngredient({…newIngredient, supplier: e.target.value})}
className=”w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent”
placeholder=”e.g., Costco”
/>
Ingredient List
{ingredients.length === 0 ? (
No ingredients added yet
) : (
| Ingredient | Amount | Cost/Unit | Total Paid | Supplier | Action |
|---|---|---|---|---|---|
| {ing.name} | {ing.weight} {ing.unit} | ${ing.costPerUnit.toFixed(2)} | ${ing.totalPaid.toFixed(2)} | {ing.supplier || ‘-‘} |
)}
)}
{/* Recipes View */}
{view === ‘recipes’ && (
Create Recipe
setNewRecipe({…newRecipe, name: e.target.value})}
className=”w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent”
placeholder=”e.g., Teriyaki Beef Jerky”
/>
setNewRecipe({…newRecipe, batchSize: e.target.value})}
className=”w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent”
placeholder=”e.g., 100 (bags/pieces)”
/>
Add Ingredients
setRecipeItem({…recipeItem, quantity: e.target.value})}
className=”w-24 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-transparent”
placeholder=”Qty”
/>
{newRecipe.items.length > 0 && (
Recipe Card
{newRecipe.name || ‘Untitled Recipe’}
Makes: {newRecipe.batchSize || ‘—’} units
Ingredients:
-
{newRecipe.items.map((item, idx) => {
-
{ing?.name}
{item.quantity} {ing?.unit}
const ing = ingredients.find(i => i.id === item.ingredientId);
return (
);
})}
)}
Saved Recipes
No recipes created yet
) : (
recipes.map((recipe) => {
const totalCost = calculateRecipeCost(recipe);
const costPerUnit = totalCost / recipe.batchSize;
return (
{recipe.name}
);
})
)}
)}
{/* Calculator View */}
{view === ‘calculator’ && (
Cost Calculator & Scenario Planning
{recipes.length === 0 ? (
Create a recipe first to use the calculator
) : (
<>
{selectedRecipe && (
{selectedRecipe.name}
setSelectedRecipe({
…selectedRecipe,
batchSize: parseFloat(e.target.value) || 0
})}
className=”mt-1 w-full px-3 py-2 border border-gray-300 rounded text-center font-bold”
/>
Ingredient Breakdown
| Ingredient | Quantity | Cost/Unit | Line Total |
|---|---|---|---|
| {ing.name} |
{ const newItems = […selectedRecipe.items]; newItems[idx].quantity = parseFloat(e.target.value) || 0; setSelectedRecipe({…selectedRecipe, items: newItems}); }} className=”w-24 px-2 py-1 border border-gray-300 rounded text-right” /> {ing.unit} |
${ing.costPerUnit.toFixed(2)}/{ing.unit} | ${itemCost.toFixed(2)} |
| Total Recipe Cost: | ${calculateRecipeCost(selectedRecipe).toFixed(2)} | ||
)}
>
)}
)}
);
}
