healthy-meals-core 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bmr.d.ts +3 -0
- package/dist/bmr.js +14 -0
- package/dist/dailyMealPlanGenerator.d.ts +3 -0
- package/dist/dailyMealPlanGenerator.js +211 -0
- package/dist/data/detailed-recipes-bs.json +417 -0
- package/dist/data/detailed-recipes.json +744 -0
- package/dist/foodConversion.d.ts +55 -0
- package/dist/foodConversion.js +200 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +27 -0
- package/dist/planner.d.ts +2 -0
- package/dist/planner.js +24 -0
- package/dist/recipeBasedMealPlanGenerator.d.ts +17 -0
- package/dist/recipeBasedMealPlanGenerator.js +255 -0
- package/dist/recipeService.d.ts +28 -0
- package/dist/recipeService.js +136 -0
- package/dist/rules/cholesterol.d.ts +2 -0
- package/dist/rules/cholesterol.js +7 -0
- package/dist/rules/diabetes.d.ts +2 -0
- package/dist/rules/diabetes.js +7 -0
- package/dist/rules/fattyLiver.d.ts +2 -0
- package/dist/rules/fattyLiver.js +7 -0
- package/dist/rules/index.d.ts +7 -0
- package/dist/rules/index.js +47 -0
- package/dist/rules/lowCarb.d.ts +2 -0
- package/dist/rules/lowCarb.js +7 -0
- package/dist/rules/triglycerides.d.ts +2 -0
- package/dist/rules/triglycerides.js +7 -0
- package/dist/src/bmr.d.ts +3 -0
- package/dist/src/bmr.js +13 -0
- package/dist/src/dailyMealPlanGenerator.d.ts +3 -0
- package/dist/src/dailyMealPlanGenerator.js +210 -0
- package/dist/src/foodConversion.d.ts +55 -0
- package/dist/src/foodConversion.js +199 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +27 -0
- package/dist/src/planner.d.ts +2 -0
- package/dist/src/planner.js +23 -0
- package/dist/src/recipeBasedMealPlanGenerator.d.ts +17 -0
- package/dist/src/recipeBasedMealPlanGenerator.js +254 -0
- package/dist/src/recipeService.d.ts +28 -0
- package/dist/src/recipeService.js +136 -0
- package/dist/src/rules/cholesterol.d.ts +2 -0
- package/dist/src/rules/cholesterol.js +6 -0
- package/dist/src/rules/diabetes.d.ts +2 -0
- package/dist/src/rules/diabetes.js +6 -0
- package/dist/src/rules/fattyLiver.d.ts +2 -0
- package/dist/src/rules/fattyLiver.js +6 -0
- package/dist/src/rules/index.d.ts +7 -0
- package/dist/src/rules/index.js +46 -0
- package/dist/src/rules/lowCarb.d.ts +2 -0
- package/dist/src/rules/lowCarb.js +6 -0
- package/dist/src/rules/triglycerides.d.ts +2 -0
- package/dist/src/rules/triglycerides.js +6 -0
- package/dist/src/types/firestore.d.ts +100 -0
- package/dist/src/types/firestore.js +2 -0
- package/dist/src/types/openfoodfacts.d.ts +113 -0
- package/dist/src/types/openfoodfacts.js +3 -0
- package/dist/src/types/recipe.d.ts +36 -0
- package/dist/src/types/recipe.js +2 -0
- package/dist/src/types.d.ts +24 -0
- package/dist/src/types.js +2 -0
- package/dist/src/variety.d.ts +17 -0
- package/dist/src/variety.js +129 -0
- package/dist/src/weeklyMealPlanGenerator.d.ts +3 -0
- package/dist/src/weeklyMealPlanGenerator.js +468 -0
- package/dist/src/weeklyPlanner.d.ts +12 -0
- package/dist/src/weeklyPlanner.js +31 -0
- package/dist/types/firestore.d.ts +100 -0
- package/dist/types/firestore.js +2 -0
- package/dist/types/openfoodfacts.d.ts +113 -0
- package/dist/types/openfoodfacts.js +3 -0
- package/dist/types/recipe.d.ts +36 -0
- package/dist/types/recipe.js +2 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.js +2 -0
- package/dist/variety.d.ts +17 -0
- package/dist/variety.js +130 -0
- package/dist/weeklyMealPlanGenerator.d.ts +3 -0
- package/dist/weeklyMealPlanGenerator.js +469 -0
- package/dist/weeklyPlanner.d.ts +12 -0
- package/dist/weeklyPlanner.js +32 -0
- package/package.json +66 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertFoodItemToIngredient = convertFoodItemToIngredient;
|
|
4
|
+
exports.calculateTotalNutrition = calculateTotalNutrition;
|
|
5
|
+
exports.createMealFromFoodItems = createMealFromFoodItems;
|
|
6
|
+
exports.getNutritionDensityScore = getNutritionDensityScore;
|
|
7
|
+
exports.checkDietaryRestrictions = checkDietaryRestrictions;
|
|
8
|
+
exports.getRecommendedServingSize = getRecommendedServingSize;
|
|
9
|
+
exports.filterFoodsByHealthGoals = filterFoodsByHealthGoals;
|
|
10
|
+
/**
|
|
11
|
+
* Convert Open Food Facts item to Ingredient for meal planning
|
|
12
|
+
*/
|
|
13
|
+
function convertFoodItemToIngredient(foodItem, amount = 100, unit = 'g') {
|
|
14
|
+
// Calculate nutrition values based on amount (default is per 100g)
|
|
15
|
+
const multiplier = amount / 100;
|
|
16
|
+
return {
|
|
17
|
+
id: `${foodItem.name.toLowerCase().replace(/\s+/g, '-')}-${Date.now()}`,
|
|
18
|
+
name: foodItem.name,
|
|
19
|
+
quantity: amount,
|
|
20
|
+
unit,
|
|
21
|
+
calories: Math.round((foodItem.nutritionPer100g?.calories || 0) * multiplier) || 0,
|
|
22
|
+
protein: Math.round(((foodItem.nutritionPer100g?.protein || 0) * multiplier * 10)) / 10 || 0,
|
|
23
|
+
carbs: Math.round(((foodItem.nutritionPer100g?.carbs || 0) * multiplier * 10)) / 10 || 0,
|
|
24
|
+
fat: Math.round(((foodItem.nutritionPer100g?.fat || 0) * multiplier * 10)) / 10 || 0,
|
|
25
|
+
fiber: Math.round(((foodItem.nutritionPer100g?.fiber || 0) * multiplier * 10)) / 10 || 0,
|
|
26
|
+
sugar: Math.round(((foodItem.nutritionPer100g?.sugar || 0) * multiplier * 10)) / 10 || 0,
|
|
27
|
+
sodium: Math.round(((foodItem.nutritionPer100g?.sodium || 0) * multiplier * 10)) / 10 || 0,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Calculate total nutrition for a list of ingredients
|
|
32
|
+
*/
|
|
33
|
+
function calculateTotalNutrition(ingredients) {
|
|
34
|
+
return ingredients.reduce((total, ingredient) => ({
|
|
35
|
+
calories: total.calories + (ingredient.calories || 0),
|
|
36
|
+
protein: Math.round((total.protein + (ingredient.protein || 0)) * 10) / 10,
|
|
37
|
+
carbs: Math.round((total.carbs + (ingredient.carbs || 0)) * 10) / 10,
|
|
38
|
+
fat: Math.round((total.fat + (ingredient.fat || 0)) * 10) / 10,
|
|
39
|
+
fiber: Math.round((total.fiber + (ingredient.fiber || 0)) * 10) / 10,
|
|
40
|
+
sugar: Math.round((total.sugar + (ingredient.sugar || 0)) * 10) / 10,
|
|
41
|
+
sodium: Math.round((total.sodium + (ingredient.sodium || 0)) * 10) / 10,
|
|
42
|
+
}), { calories: 0, protein: 0, carbs: 0, fat: 0, fiber: 0, sugar: 0, sodium: 0 });
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a meal from selected food items
|
|
46
|
+
*/
|
|
47
|
+
function createMealFromFoodItems(name, type, foodItems) {
|
|
48
|
+
const ingredients = foodItems.map(({ food, amount, unit }) => convertFoodItemToIngredient(food, amount, unit));
|
|
49
|
+
const nutrition = calculateTotalNutrition(ingredients);
|
|
50
|
+
return {
|
|
51
|
+
id: `${type}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
52
|
+
name,
|
|
53
|
+
type,
|
|
54
|
+
ingredients,
|
|
55
|
+
nutrition: {
|
|
56
|
+
calories: nutrition.calories,
|
|
57
|
+
protein: nutrition.protein,
|
|
58
|
+
carbs: nutrition.carbs,
|
|
59
|
+
fat: nutrition.fat,
|
|
60
|
+
fiber: nutrition.fiber,
|
|
61
|
+
sugar: nutrition.sugar,
|
|
62
|
+
sodium: nutrition.sodium,
|
|
63
|
+
},
|
|
64
|
+
instructions: [],
|
|
65
|
+
prepTime: 15,
|
|
66
|
+
cookTime: 20,
|
|
67
|
+
servings: 1,
|
|
68
|
+
tags: [type],
|
|
69
|
+
difficulty: 'easy',
|
|
70
|
+
createdAt: new Date(),
|
|
71
|
+
updatedAt: new Date(),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get nutrition density score (protein per calorie ratio)
|
|
76
|
+
*/
|
|
77
|
+
function getNutritionDensityScore(foodItem) {
|
|
78
|
+
if (foodItem.nutritionPer100g.calories === 0)
|
|
79
|
+
return 0;
|
|
80
|
+
return foodItem.nutritionPer100g.protein / foodItem.nutritionPer100g.calories;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if food item meets dietary restrictions
|
|
84
|
+
*/
|
|
85
|
+
function checkDietaryRestrictions(foodItem, restrictions) {
|
|
86
|
+
const violations = [];
|
|
87
|
+
// Check categories for dietary restrictions
|
|
88
|
+
const categories = foodItem.categories?.map(c => c.toLowerCase()) || [];
|
|
89
|
+
const ingredients = foodItem.ingredients?.toLowerCase() || '';
|
|
90
|
+
const allergens = foodItem.allergens?.map(a => a.toLowerCase()) || [];
|
|
91
|
+
restrictions.forEach(restriction => {
|
|
92
|
+
const restrictionLower = restriction.toLowerCase();
|
|
93
|
+
switch (restrictionLower) {
|
|
94
|
+
case 'vegetarian':
|
|
95
|
+
if (categories.some((cat) => cat.includes('meat') || cat.includes('fish') || cat.includes('poultry'))) {
|
|
96
|
+
violations.push('Contains meat/fish');
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case 'vegan':
|
|
100
|
+
if (categories.some((cat) => cat.includes('meat') || cat.includes('fish') || cat.includes('dairy') ||
|
|
101
|
+
cat.includes('eggs') || cat.includes('honey')) || ingredients.includes('milk') || ingredients.includes('egg')) {
|
|
102
|
+
violations.push('Contains animal products');
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
case 'gluten-free':
|
|
106
|
+
if (allergens.includes('gluten') || ingredients.includes('wheat') ||
|
|
107
|
+
ingredients.includes('barley') || ingredients.includes('rye')) {
|
|
108
|
+
violations.push('Contains gluten');
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
case 'dairy-free':
|
|
112
|
+
if (allergens.includes('milk') || categories.some((cat) => cat.includes('dairy')) ||
|
|
113
|
+
ingredients.includes('milk') || ingredients.includes('lactose')) {
|
|
114
|
+
violations.push('Contains dairy');
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
case 'nut-free':
|
|
118
|
+
if (allergens.some((a) => a.includes('nut')) || ingredients.includes('nuts')) {
|
|
119
|
+
violations.push('Contains nuts');
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
meets: violations.length === 0,
|
|
126
|
+
violations
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get recommended serving size based on meal type and nutrition goals
|
|
131
|
+
*/
|
|
132
|
+
function getRecommendedServingSize(foodItem, mealType, targetCalories) {
|
|
133
|
+
// Default serving size from product data
|
|
134
|
+
if (foodItem.servingSize) {
|
|
135
|
+
const match = foodItem.servingSize.match(/(\d+(?:\.\d+)?)\s*(\w+)/);
|
|
136
|
+
if (match) {
|
|
137
|
+
return {
|
|
138
|
+
amount: parseFloat(match[1]),
|
|
139
|
+
unit: match[2]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Calculate based on target calories if provided
|
|
144
|
+
if (targetCalories && foodItem.nutritionPer100g.calories > 0) {
|
|
145
|
+
const amount = Math.round((targetCalories / foodItem.nutritionPer100g.calories) * 100);
|
|
146
|
+
return { amount, unit: 'g' };
|
|
147
|
+
}
|
|
148
|
+
// Default serving sizes by meal type
|
|
149
|
+
const defaultServings = {
|
|
150
|
+
breakfast: 150, // g
|
|
151
|
+
lunch: 200, // g
|
|
152
|
+
dinner: 200, // g
|
|
153
|
+
snack: 50 // g
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
amount: defaultServings[mealType],
|
|
157
|
+
unit: 'g'
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Filter foods by health goals
|
|
162
|
+
*/
|
|
163
|
+
function filterFoodsByHealthGoals(foods, healthGoals) {
|
|
164
|
+
return foods.filter(food => {
|
|
165
|
+
for (const goal of healthGoals) {
|
|
166
|
+
switch (goal.toLowerCase()) {
|
|
167
|
+
case 'weight_loss':
|
|
168
|
+
// Prefer low-calorie, high-protein foods
|
|
169
|
+
if (food.nutritionPer100g.calories > 300 ||
|
|
170
|
+
getNutritionDensityScore(food) < 0.05) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
case 'muscle_gain':
|
|
175
|
+
// Prefer high-protein foods
|
|
176
|
+
if (food.nutritionPer100g.protein < 10) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
case 'heart_health':
|
|
181
|
+
// Prefer foods with good nutrition grades and low sodium
|
|
182
|
+
if (food.nutritionGrade && !['a', 'b'].includes(food.nutritionGrade.toLowerCase())) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
if (food.nutritionPer100g.sodium && food.nutritionPer100g.sodium > 600) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
case 'diabetes_management':
|
|
190
|
+
// Prefer low-sugar, high-fiber foods
|
|
191
|
+
if (food.nutritionPer100g.sugar && food.nutritionPer100g.sugar > 15) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return true;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { UserProfile, HealthCondition, Gender } from './types';
|
|
2
|
+
export * from './bmr';
|
|
3
|
+
export * from './planner';
|
|
4
|
+
export * from './rules';
|
|
5
|
+
export * from './weeklyPlanner';
|
|
6
|
+
export * from './variety';
|
|
7
|
+
export * from './dailyMealPlanGenerator';
|
|
8
|
+
export * from './weeklyMealPlanGenerator';
|
|
9
|
+
export * from './foodConversion';
|
|
10
|
+
export * from './types/recipe';
|
|
11
|
+
export * from './recipeService';
|
|
12
|
+
export * from './recipeBasedMealPlanGenerator';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./bmr"), exports);
|
|
18
|
+
__exportStar(require("./planner"), exports);
|
|
19
|
+
__exportStar(require("./rules"), exports);
|
|
20
|
+
__exportStar(require("./weeklyPlanner"), exports);
|
|
21
|
+
__exportStar(require("./variety"), exports);
|
|
22
|
+
__exportStar(require("./dailyMealPlanGenerator"), exports);
|
|
23
|
+
__exportStar(require("./weeklyMealPlanGenerator"), exports);
|
|
24
|
+
__exportStar(require("./foodConversion"), exports);
|
|
25
|
+
__exportStar(require("./types/recipe"), exports);
|
|
26
|
+
__exportStar(require("./recipeService"), exports);
|
|
27
|
+
__exportStar(require("./recipeBasedMealPlanGenerator"), exports);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateDailyPlan = generateDailyPlan;
|
|
4
|
+
const bmr_1 = require("./bmr");
|
|
5
|
+
const rules_1 = require("./rules");
|
|
6
|
+
function generateDailyPlan(profile, recipes) {
|
|
7
|
+
const targetCalories = Math.round((0, bmr_1.calculateTDEE)(profile));
|
|
8
|
+
const filtered = (0, rules_1.applyHealthRules)(recipes, profile.conditions);
|
|
9
|
+
let total = 0;
|
|
10
|
+
const meals = [];
|
|
11
|
+
for (const recipe of filtered) {
|
|
12
|
+
if (total + recipe.calories <= targetCalories) {
|
|
13
|
+
meals.push(recipe);
|
|
14
|
+
total += recipe.calories;
|
|
15
|
+
}
|
|
16
|
+
if (total >= targetCalories * 0.95)
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
dailyCalories: total,
|
|
21
|
+
meals
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { UserProfile, MealPlan, Meal } from './types/firestore';
|
|
2
|
+
import { Recipe } from './types/recipe';
|
|
3
|
+
/**
|
|
4
|
+
* Generate a daily meal plan using recipes from the recipe database
|
|
5
|
+
* Each meal will have complete ingredients and cooking instructions
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateDailyMealPlanWithRecipes(profile: UserProfile, userId: string): MealPlan;
|
|
8
|
+
/**
|
|
9
|
+
* Generate a weekly meal plan using recipes from the recipe database
|
|
10
|
+
* Each day will have 4 meals with complete ingredients and cooking instructions
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateWeeklyMealPlanWithRecipes(profile: UserProfile, userId: string): MealPlan;
|
|
13
|
+
/**
|
|
14
|
+
* Get recipe details for a meal in a meal plan
|
|
15
|
+
* This allows you to retrieve the full recipe with instructions for any meal
|
|
16
|
+
*/
|
|
17
|
+
export declare function getRecipeForMeal(meal: Meal): Recipe | undefined;
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateDailyMealPlanWithRecipes = generateDailyMealPlanWithRecipes;
|
|
4
|
+
exports.generateWeeklyMealPlanWithRecipes = generateWeeklyMealPlanWithRecipes;
|
|
5
|
+
exports.getRecipeForMeal = getRecipeForMeal;
|
|
6
|
+
const recipeService_1 = require("./recipeService");
|
|
7
|
+
function calculateDailyNeeds(profile) {
|
|
8
|
+
const age = profile.age || 30;
|
|
9
|
+
const height = profile.height || 170;
|
|
10
|
+
const weight = profile.weight || 70;
|
|
11
|
+
const gender = profile.gender || 'male';
|
|
12
|
+
const activityLevel = profile.activityLevel || 'moderately_active';
|
|
13
|
+
const bmr = gender === 'male'
|
|
14
|
+
? 88.362 + (13.397 * weight) + (4.799 * height) - (5.677 * age)
|
|
15
|
+
: 447.593 + (9.247 * weight) + (3.098 * height) - (4.330 * age);
|
|
16
|
+
const activityMultipliers = {
|
|
17
|
+
sedentary: 1.2,
|
|
18
|
+
lightly_active: 1.375,
|
|
19
|
+
moderately_active: 1.55,
|
|
20
|
+
very_active: 1.725,
|
|
21
|
+
extremely_active: 1.9
|
|
22
|
+
};
|
|
23
|
+
const tdee = bmr * (activityMultipliers[activityLevel] || 1.2);
|
|
24
|
+
let dailyCalories = tdee;
|
|
25
|
+
if (profile.healthGoals?.includes('weight_loss')) {
|
|
26
|
+
dailyCalories -= 500;
|
|
27
|
+
}
|
|
28
|
+
else if (profile.healthGoals?.includes('weight_gain')) {
|
|
29
|
+
dailyCalories += 500;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
calories: Math.round(Math.max(dailyCalories, 1200)),
|
|
33
|
+
protein: Math.round(Math.max(dailyCalories * 0.3 / 4, 50)),
|
|
34
|
+
carbs: Math.round(Math.max(dailyCalories * 0.4 / 4, 100)),
|
|
35
|
+
fat: Math.round(Math.max(dailyCalories * 0.3 / 9, 30))
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function convertRecipeToMeal(recipe) {
|
|
39
|
+
return {
|
|
40
|
+
id: recipe.id,
|
|
41
|
+
name: recipe.name,
|
|
42
|
+
type: recipe.category,
|
|
43
|
+
ingredients: recipe.ingredients.map(ing => ({
|
|
44
|
+
id: `${recipe.id}-${ing.name}`,
|
|
45
|
+
name: ing.name,
|
|
46
|
+
quantity: ing.quantity,
|
|
47
|
+
unit: ing.unit,
|
|
48
|
+
calories: 0,
|
|
49
|
+
protein: 0,
|
|
50
|
+
carbs: 0,
|
|
51
|
+
fat: 0,
|
|
52
|
+
fiber: 0,
|
|
53
|
+
sugar: 0,
|
|
54
|
+
sodium: 0
|
|
55
|
+
})),
|
|
56
|
+
instructions: recipe.instructions,
|
|
57
|
+
prepTime: recipe.prepTime,
|
|
58
|
+
cookTime: recipe.cookTime,
|
|
59
|
+
servings: recipe.servings,
|
|
60
|
+
nutrition: {
|
|
61
|
+
calories: recipe.nutrition.calories,
|
|
62
|
+
protein: recipe.nutrition.protein,
|
|
63
|
+
carbs: recipe.nutrition.carbs,
|
|
64
|
+
fat: recipe.nutrition.fat,
|
|
65
|
+
fiber: recipe.nutrition.fiber,
|
|
66
|
+
sugar: recipe.nutrition.sugar,
|
|
67
|
+
sodium: recipe.nutrition.sodium
|
|
68
|
+
},
|
|
69
|
+
tags: recipe.tags,
|
|
70
|
+
difficulty: recipe.difficulty,
|
|
71
|
+
createdAt: new Date(),
|
|
72
|
+
updatedAt: new Date()
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate a daily meal plan using recipes from the recipe database
|
|
77
|
+
* Each meal will have complete ingredients and cooking instructions
|
|
78
|
+
*/
|
|
79
|
+
function generateDailyMealPlanWithRecipes(profile, userId) {
|
|
80
|
+
const needs = calculateDailyNeeds(profile);
|
|
81
|
+
// Get recipes filtered by user preferences
|
|
82
|
+
let availableRecipes = recipeService_1.recipeService.getAllRecipes();
|
|
83
|
+
// Filter by dietary restrictions
|
|
84
|
+
if (profile.dietaryRestrictions && profile.dietaryRestrictions.length > 0) {
|
|
85
|
+
availableRecipes = recipeService_1.recipeService.getRecipesForDietaryRestrictions(profile.dietaryRestrictions);
|
|
86
|
+
}
|
|
87
|
+
// Filter by health goals
|
|
88
|
+
if (profile.healthGoals && profile.healthGoals.length > 0) {
|
|
89
|
+
const healthRecipes = recipeService_1.recipeService.getRecipesForHealthGoals(profile.healthGoals);
|
|
90
|
+
availableRecipes = availableRecipes.filter(r => healthRecipes.some(hr => hr.id === r.id));
|
|
91
|
+
}
|
|
92
|
+
// Filter by cooking time preference if available
|
|
93
|
+
if (profile.preferences?.cookingTime) {
|
|
94
|
+
availableRecipes = availableRecipes.filter(r => recipeService_1.recipeService.getTotalTime(r) <= profile.preferences.cookingTime);
|
|
95
|
+
}
|
|
96
|
+
// Select one recipe for each meal type
|
|
97
|
+
const breakfastRecipes = availableRecipes.filter(r => r.category === 'breakfast');
|
|
98
|
+
const lunchRecipes = availableRecipes.filter(r => r.category === 'lunch');
|
|
99
|
+
const dinnerRecipes = availableRecipes.filter(r => r.category === 'dinner');
|
|
100
|
+
const snackRecipes = availableRecipes.filter(r => r.category === 'snack');
|
|
101
|
+
// Pick random recipes or first available
|
|
102
|
+
const selectedRecipes = {
|
|
103
|
+
breakfast: breakfastRecipes.length > 0
|
|
104
|
+
? breakfastRecipes[Math.floor(Math.random() * breakfastRecipes.length)]
|
|
105
|
+
: recipeService_1.recipeService.getRecipeById('breakfast-1'),
|
|
106
|
+
lunch: lunchRecipes.length > 0
|
|
107
|
+
? lunchRecipes[Math.floor(Math.random() * lunchRecipes.length)]
|
|
108
|
+
: recipeService_1.recipeService.getRecipeById('lunch-1'),
|
|
109
|
+
dinner: dinnerRecipes.length > 0
|
|
110
|
+
? dinnerRecipes[Math.floor(Math.random() * dinnerRecipes.length)]
|
|
111
|
+
: recipeService_1.recipeService.getRecipeById('dinner-1'),
|
|
112
|
+
snack: snackRecipes.length > 0
|
|
113
|
+
? snackRecipes[Math.floor(Math.random() * snackRecipes.length)]
|
|
114
|
+
: recipeService_1.recipeService.getRecipeById('snack-1')
|
|
115
|
+
};
|
|
116
|
+
// Convert recipes to meals
|
|
117
|
+
const meals = [
|
|
118
|
+
convertRecipeToMeal(selectedRecipes.breakfast),
|
|
119
|
+
convertRecipeToMeal(selectedRecipes.lunch),
|
|
120
|
+
convertRecipeToMeal(selectedRecipes.dinner),
|
|
121
|
+
convertRecipeToMeal(selectedRecipes.snack)
|
|
122
|
+
];
|
|
123
|
+
// Calculate total nutrition
|
|
124
|
+
const totalNutrition = meals.reduce((total, meal) => ({
|
|
125
|
+
calories: total.calories + (meal.nutrition?.calories || 0),
|
|
126
|
+
protein: Math.round((total.protein + (meal.nutrition?.protein || 0)) * 10) / 10,
|
|
127
|
+
carbs: Math.round((total.carbs + (meal.nutrition?.carbs || 0)) * 10) / 10,
|
|
128
|
+
fat: Math.round((total.fat + (meal.nutrition?.fat || 0)) * 10) / 10
|
|
129
|
+
}), { calories: 0, protein: 0, carbs: 0, fat: 0 });
|
|
130
|
+
const today = new Date();
|
|
131
|
+
return {
|
|
132
|
+
id: `daily-recipe-plan-${Date.now()}`,
|
|
133
|
+
userId: userId,
|
|
134
|
+
name: `Daily Meal Plan - ${today.toLocaleDateString()}`,
|
|
135
|
+
description: `Personalized daily meal plan with complete recipes (${Math.round(totalNutrition.calories)} cal)`,
|
|
136
|
+
startDate: today.toISOString().split('T')[0],
|
|
137
|
+
endDate: today.toISOString().split('T')[0],
|
|
138
|
+
dailyPlans: [],
|
|
139
|
+
meals,
|
|
140
|
+
totalCalories: totalNutrition.calories,
|
|
141
|
+
totalProtein: totalNutrition.protein,
|
|
142
|
+
totalCarbs: totalNutrition.carbs,
|
|
143
|
+
totalFat: totalNutrition.fat,
|
|
144
|
+
uid: userId,
|
|
145
|
+
goals: {
|
|
146
|
+
dailyCalories: needs.calories,
|
|
147
|
+
proteinPercentage: 25,
|
|
148
|
+
carbsPercentage: 45,
|
|
149
|
+
fatPercentage: 30,
|
|
150
|
+
},
|
|
151
|
+
createdAt: new Date(),
|
|
152
|
+
updatedAt: new Date()
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Generate a weekly meal plan using recipes from the recipe database
|
|
157
|
+
* Each day will have 4 meals with complete ingredients and cooking instructions
|
|
158
|
+
*/
|
|
159
|
+
function generateWeeklyMealPlanWithRecipes(profile, userId) {
|
|
160
|
+
const needs = calculateDailyNeeds(profile);
|
|
161
|
+
// Get recipes filtered by user preferences
|
|
162
|
+
let availableRecipes = recipeService_1.recipeService.getAllRecipes();
|
|
163
|
+
// Filter by dietary restrictions
|
|
164
|
+
if (profile.dietaryRestrictions && profile.dietaryRestrictions.length > 0) {
|
|
165
|
+
availableRecipes = recipeService_1.recipeService.getRecipesForDietaryRestrictions(profile.dietaryRestrictions);
|
|
166
|
+
}
|
|
167
|
+
// Filter by health goals
|
|
168
|
+
if (profile.healthGoals && profile.healthGoals.length > 0) {
|
|
169
|
+
const healthRecipes = recipeService_1.recipeService.getRecipesForHealthGoals(profile.healthGoals);
|
|
170
|
+
availableRecipes = availableRecipes.filter(r => healthRecipes.some(hr => hr.id === r.id));
|
|
171
|
+
}
|
|
172
|
+
// Filter by cooking time preference
|
|
173
|
+
if (profile.preferences?.cookingTime) {
|
|
174
|
+
availableRecipes = availableRecipes.filter(r => recipeService_1.recipeService.getTotalTime(r) <= profile.preferences.cookingTime);
|
|
175
|
+
}
|
|
176
|
+
// Separate by category
|
|
177
|
+
const breakfastRecipes = availableRecipes.filter(r => r.category === 'breakfast');
|
|
178
|
+
const lunchRecipes = availableRecipes.filter(r => r.category === 'lunch');
|
|
179
|
+
const dinnerRecipes = availableRecipes.filter(r => r.category === 'dinner');
|
|
180
|
+
const snackRecipes = availableRecipes.filter(r => r.category === 'snack');
|
|
181
|
+
const allMeals = [];
|
|
182
|
+
const daysOfWeek = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
|
183
|
+
// Generate meals for each day
|
|
184
|
+
daysOfWeek.forEach((day, dayIndex) => {
|
|
185
|
+
// Select different recipes for variety (cycle through available recipes)
|
|
186
|
+
const breakfast = breakfastRecipes.length > 0
|
|
187
|
+
? breakfastRecipes[dayIndex % breakfastRecipes.length]
|
|
188
|
+
: recipeService_1.recipeService.getRecipeById('breakfast-1');
|
|
189
|
+
const lunch = lunchRecipes.length > 0
|
|
190
|
+
? lunchRecipes[dayIndex % lunchRecipes.length]
|
|
191
|
+
: recipeService_1.recipeService.getRecipeById('lunch-1');
|
|
192
|
+
const dinner = dinnerRecipes.length > 0
|
|
193
|
+
? dinnerRecipes[dayIndex % dinnerRecipes.length]
|
|
194
|
+
: recipeService_1.recipeService.getRecipeById('dinner-1');
|
|
195
|
+
const snack = snackRecipes.length > 0
|
|
196
|
+
? snackRecipes[dayIndex % snackRecipes.length]
|
|
197
|
+
: recipeService_1.recipeService.getRecipeById('snack-1');
|
|
198
|
+
// Convert to meals and add day prefix
|
|
199
|
+
const dayMeals = [
|
|
200
|
+
{ ...convertRecipeToMeal(breakfast), name: `${day} - ${breakfast.name}` },
|
|
201
|
+
{ ...convertRecipeToMeal(lunch), name: `${day} - ${lunch.name}` },
|
|
202
|
+
{ ...convertRecipeToMeal(dinner), name: `${day} - ${dinner.name}` },
|
|
203
|
+
{ ...convertRecipeToMeal(snack), name: `${day} - ${snack.name}` }
|
|
204
|
+
];
|
|
205
|
+
allMeals.push(...dayMeals);
|
|
206
|
+
});
|
|
207
|
+
// Calculate total nutrition
|
|
208
|
+
const totalNutrition = allMeals.reduce((total, meal) => ({
|
|
209
|
+
calories: total.calories + (meal.nutrition?.calories || 0),
|
|
210
|
+
protein: Math.round((total.protein + (meal.nutrition?.protein || 0)) * 10) / 10,
|
|
211
|
+
carbs: Math.round((total.carbs + (meal.nutrition?.carbs || 0)) * 10) / 10,
|
|
212
|
+
fat: Math.round((total.fat + (meal.nutrition?.fat || 0)) * 10) / 10
|
|
213
|
+
}), { calories: 0, protein: 0, carbs: 0, fat: 0 });
|
|
214
|
+
const startDate = new Date();
|
|
215
|
+
const endDate = new Date();
|
|
216
|
+
endDate.setDate(startDate.getDate() + 6);
|
|
217
|
+
return {
|
|
218
|
+
id: `weekly-recipe-plan-${Date.now()}`,
|
|
219
|
+
userId: userId,
|
|
220
|
+
name: `Weekly Meal Plan - ${startDate.toLocaleDateString()} to ${endDate.toLocaleDateString()}`,
|
|
221
|
+
description: `7-day meal plan with complete recipes and instructions (${Math.round(totalNutrition.calories / 7)} cal/day avg)`,
|
|
222
|
+
startDate: startDate.toISOString().split('T')[0],
|
|
223
|
+
endDate: endDate.toISOString().split('T')[0],
|
|
224
|
+
dailyPlans: [],
|
|
225
|
+
meals: allMeals,
|
|
226
|
+
totalCalories: totalNutrition.calories,
|
|
227
|
+
totalProtein: totalNutrition.protein,
|
|
228
|
+
totalCarbs: totalNutrition.carbs,
|
|
229
|
+
totalFat: totalNutrition.fat,
|
|
230
|
+
uid: userId,
|
|
231
|
+
goals: {
|
|
232
|
+
dailyCalories: needs.calories,
|
|
233
|
+
proteinPercentage: 25,
|
|
234
|
+
carbsPercentage: 45,
|
|
235
|
+
fatPercentage: 30,
|
|
236
|
+
},
|
|
237
|
+
createdAt: new Date(),
|
|
238
|
+
updatedAt: new Date()
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get recipe details for a meal in a meal plan
|
|
243
|
+
* This allows you to retrieve the full recipe with instructions for any meal
|
|
244
|
+
*/
|
|
245
|
+
function getRecipeForMeal(meal) {
|
|
246
|
+
// Try to find the recipe by ID (if meal ID matches recipe ID)
|
|
247
|
+
const recipe = recipeService_1.recipeService.getRecipeById(meal.id);
|
|
248
|
+
if (recipe) {
|
|
249
|
+
return recipe;
|
|
250
|
+
}
|
|
251
|
+
// Try to find by name (remove day prefix if present)
|
|
252
|
+
const cleanName = meal.name.replace(/^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday) - /, '');
|
|
253
|
+
return recipeService_1.recipeService.searchRecipes(cleanName)[0];
|
|
254
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Recipe, RecipeFilter } from './types/recipe';
|
|
2
|
+
export { RecipeFilter };
|
|
3
|
+
export declare class RecipeService {
|
|
4
|
+
private recipes;
|
|
5
|
+
private recipesBS;
|
|
6
|
+
private currentLocale;
|
|
7
|
+
constructor(locale?: string);
|
|
8
|
+
setLocale(locale: string): void;
|
|
9
|
+
private getCurrentRecipes;
|
|
10
|
+
getAllRecipes(): Recipe[];
|
|
11
|
+
getRecipeById(id: string): Recipe | undefined;
|
|
12
|
+
getRecipesByCategory(category: 'breakfast' | 'lunch' | 'dinner' | 'snack'): Recipe[];
|
|
13
|
+
filterRecipes(filter: RecipeFilter): Recipe[];
|
|
14
|
+
getRecipesByTags(tags: string[]): Recipe[];
|
|
15
|
+
getRecipesForHealthGoals(healthGoals: string[]): Recipe[];
|
|
16
|
+
getRecipesForDietaryRestrictions(restrictions: string[]): Recipe[];
|
|
17
|
+
getRandomRecipe(category?: 'breakfast' | 'lunch' | 'dinner' | 'snack'): Recipe;
|
|
18
|
+
getWeeklyRecipeSuggestions(healthGoals: string[], dietaryRestrictions: string[]): {
|
|
19
|
+
breakfast: Recipe[];
|
|
20
|
+
lunch: Recipe[];
|
|
21
|
+
dinner: Recipe[];
|
|
22
|
+
snack: Recipe[];
|
|
23
|
+
};
|
|
24
|
+
searchRecipes(query: string): Recipe[];
|
|
25
|
+
getTotalTime(recipe: Recipe): number;
|
|
26
|
+
getRecipesByTotalTime(maxMinutes: number): Recipe[];
|
|
27
|
+
}
|
|
28
|
+
export declare const recipeService: RecipeService;
|