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,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.recipeService = exports.RecipeService = void 0;
|
|
7
|
+
const detailed_recipes_json_1 = __importDefault(require("../data/detailed-recipes.json"));
|
|
8
|
+
const detailed_recipes_bs_json_1 = __importDefault(require("../data/detailed-recipes-bs.json"));
|
|
9
|
+
class RecipeService {
|
|
10
|
+
constructor(locale = 'en') {
|
|
11
|
+
this.currentLocale = 'en';
|
|
12
|
+
this.recipes = detailed_recipes_json_1.default;
|
|
13
|
+
this.recipesBS = detailed_recipes_bs_json_1.default;
|
|
14
|
+
this.currentLocale = locale;
|
|
15
|
+
}
|
|
16
|
+
setLocale(locale) {
|
|
17
|
+
this.currentLocale = locale;
|
|
18
|
+
}
|
|
19
|
+
getCurrentRecipes() {
|
|
20
|
+
return this.currentLocale === 'bs' ? this.recipesBS : this.recipes;
|
|
21
|
+
}
|
|
22
|
+
getAllRecipes() {
|
|
23
|
+
return this.getCurrentRecipes();
|
|
24
|
+
}
|
|
25
|
+
getRecipeById(id) {
|
|
26
|
+
return this.getCurrentRecipes().find(recipe => recipe.id === id);
|
|
27
|
+
}
|
|
28
|
+
getRecipesByCategory(category) {
|
|
29
|
+
return this.getCurrentRecipes().filter(recipe => recipe.category === category);
|
|
30
|
+
}
|
|
31
|
+
filterRecipes(filter) {
|
|
32
|
+
return this.getCurrentRecipes().filter(recipe => {
|
|
33
|
+
if (filter.category && recipe.category !== filter.category) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
37
|
+
const hasMatchingTag = filter.tags.some(tag => recipe.tags.includes(tag));
|
|
38
|
+
if (!hasMatchingTag) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (filter.maxPrepTime && recipe.prepTime > filter.maxPrepTime) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
if (filter.maxCookTime && recipe.cookTime > filter.maxCookTime) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (filter.difficulty && recipe.difficulty !== filter.difficulty) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (filter.maxCalories && recipe.nutrition.calories > filter.maxCalories) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (filter.minProtein && recipe.nutrition.protein < filter.minProtein) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
getRecipesByTags(tags) {
|
|
61
|
+
return this.filterRecipes({ tags });
|
|
62
|
+
}
|
|
63
|
+
getRecipesForHealthGoals(healthGoals) {
|
|
64
|
+
const relevantTags = healthGoals.map(goal => {
|
|
65
|
+
switch (goal) {
|
|
66
|
+
case 'weight_loss':
|
|
67
|
+
return ['low-carb', 'high-protein', 'low-fat'];
|
|
68
|
+
case 'muscle_gain':
|
|
69
|
+
return ['high-protein'];
|
|
70
|
+
case 'heart_health':
|
|
71
|
+
return ['heart-healthy', 'omega-3', 'cholesterol'];
|
|
72
|
+
case 'diabetes':
|
|
73
|
+
return ['diabetes', 'low-carb'];
|
|
74
|
+
case 'cholesterol':
|
|
75
|
+
return ['cholesterol', 'heart-healthy'];
|
|
76
|
+
case 'fatty_liver':
|
|
77
|
+
return ['fatty_liver', 'low-fat'];
|
|
78
|
+
case 'triglycerides':
|
|
79
|
+
return ['triglycerides', 'omega-3'];
|
|
80
|
+
default:
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}).flat();
|
|
84
|
+
return this.filterRecipes({ tags: relevantTags });
|
|
85
|
+
}
|
|
86
|
+
getRecipesForDietaryRestrictions(restrictions) {
|
|
87
|
+
return this.getCurrentRecipes().filter(recipe => {
|
|
88
|
+
if (restrictions.includes('vegetarian')) {
|
|
89
|
+
return recipe.tags.includes('vegetarian') || recipe.tags.includes('vegan');
|
|
90
|
+
}
|
|
91
|
+
if (restrictions.includes('vegan')) {
|
|
92
|
+
return recipe.tags.includes('vegan');
|
|
93
|
+
}
|
|
94
|
+
if (restrictions.includes('gluten-free')) {
|
|
95
|
+
return recipe.tags.includes('gluten-free');
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
getRandomRecipe(category) {
|
|
101
|
+
const filteredRecipes = category
|
|
102
|
+
? this.getRecipesByCategory(category)
|
|
103
|
+
: this.getCurrentRecipes();
|
|
104
|
+
const randomIndex = Math.floor(Math.random() * filteredRecipes.length);
|
|
105
|
+
return filteredRecipes[randomIndex];
|
|
106
|
+
}
|
|
107
|
+
getWeeklyRecipeSuggestions(healthGoals, dietaryRestrictions) {
|
|
108
|
+
let availableRecipes = this.getCurrentRecipes();
|
|
109
|
+
if (dietaryRestrictions.length > 0) {
|
|
110
|
+
availableRecipes = this.getRecipesForDietaryRestrictions(dietaryRestrictions);
|
|
111
|
+
}
|
|
112
|
+
if (healthGoals.length > 0) {
|
|
113
|
+
const healthRecipes = this.getRecipesForHealthGoals(healthGoals);
|
|
114
|
+
availableRecipes = availableRecipes.filter(recipe => healthRecipes.some(hr => hr.id === recipe.id));
|
|
115
|
+
}
|
|
116
|
+
const breakfast = availableRecipes.filter(r => r.category === 'breakfast').slice(0, 7);
|
|
117
|
+
const lunch = availableRecipes.filter(r => r.category === 'lunch').slice(0, 7);
|
|
118
|
+
const dinner = availableRecipes.filter(r => r.category === 'dinner').slice(0, 7);
|
|
119
|
+
const snack = availableRecipes.filter(r => r.category === 'snack').slice(0, 7);
|
|
120
|
+
return { breakfast, lunch, dinner, snack };
|
|
121
|
+
}
|
|
122
|
+
searchRecipes(query) {
|
|
123
|
+
const lowerQuery = query.toLowerCase();
|
|
124
|
+
return this.getCurrentRecipes().filter(recipe => recipe.name.toLowerCase().includes(lowerQuery) ||
|
|
125
|
+
recipe.ingredients.some(ing => ing.name.toLowerCase().includes(lowerQuery)) ||
|
|
126
|
+
recipe.tags.some(tag => tag.toLowerCase().includes(lowerQuery)));
|
|
127
|
+
}
|
|
128
|
+
getTotalTime(recipe) {
|
|
129
|
+
return recipe.prepTime + recipe.cookTime;
|
|
130
|
+
}
|
|
131
|
+
getRecipesByTotalTime(maxMinutes) {
|
|
132
|
+
return this.recipes.filter(recipe => this.getTotalTime(recipe) <= maxMinutes);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.RecipeService = RecipeService;
|
|
136
|
+
exports.recipeService = new RecipeService();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.filterCholesterol = void 0;
|
|
4
|
+
function filterCholesterol(recipes) {
|
|
5
|
+
return recipes.filter(r => r.tags.includes('cholesterol'));
|
|
6
|
+
}
|
|
7
|
+
exports.filterCholesterol = filterCholesterol;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Recipe, HealthCondition } from '../types';
|
|
2
|
+
export declare function applyHealthRules(recipes: Recipe[], conditions: HealthCondition[]): Recipe[];
|
|
3
|
+
export * from './cholesterol';
|
|
4
|
+
export * from './triglycerides';
|
|
5
|
+
export * from './fattyLiver';
|
|
6
|
+
export * from './diabetes';
|
|
7
|
+
export * from './lowCarb';
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
exports.applyHealthRules = void 0;
|
|
18
|
+
const cholesterol_1 = require("./cholesterol");
|
|
19
|
+
const triglycerides_1 = require("./triglycerides");
|
|
20
|
+
const fattyLiver_1 = require("./fattyLiver");
|
|
21
|
+
const diabetes_1 = require("./diabetes");
|
|
22
|
+
const lowCarb_1 = require("./lowCarb");
|
|
23
|
+
function applyHealthRules(recipes, conditions) {
|
|
24
|
+
let result = recipes;
|
|
25
|
+
if (conditions.includes('cholesterol')) {
|
|
26
|
+
result = (0, cholesterol_1.filterCholesterol)(result);
|
|
27
|
+
}
|
|
28
|
+
if (conditions.includes('triglycerides')) {
|
|
29
|
+
result = (0, triglycerides_1.filterTriglycerides)(result);
|
|
30
|
+
}
|
|
31
|
+
if (conditions.includes('fatty_liver')) {
|
|
32
|
+
result = (0, fattyLiver_1.filterFattyLiver)(result);
|
|
33
|
+
}
|
|
34
|
+
if (conditions.includes('diabetes')) {
|
|
35
|
+
result = (0, diabetes_1.filterDiabetes)(result);
|
|
36
|
+
}
|
|
37
|
+
if (conditions.includes('low_carb')) {
|
|
38
|
+
result = (0, lowCarb_1.filterLowCarb)(result);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
exports.applyHealthRules = applyHealthRules;
|
|
43
|
+
__exportStar(require("./cholesterol"), exports);
|
|
44
|
+
__exportStar(require("./triglycerides"), exports);
|
|
45
|
+
__exportStar(require("./fattyLiver"), exports);
|
|
46
|
+
__exportStar(require("./diabetes"), exports);
|
|
47
|
+
__exportStar(require("./lowCarb"), exports);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.filterTriglycerides = void 0;
|
|
4
|
+
function filterTriglycerides(recipes) {
|
|
5
|
+
return recipes.filter(r => r.tags.includes('triglycerides'));
|
|
6
|
+
}
|
|
7
|
+
exports.filterTriglycerides = filterTriglycerides;
|
package/dist/src/bmr.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateBMR = calculateBMR;
|
|
4
|
+
exports.calculateTDEE = calculateTDEE;
|
|
5
|
+
function calculateBMR(profile) {
|
|
6
|
+
const { weightKg, heightCm, age, gender } = profile;
|
|
7
|
+
return gender === 'male'
|
|
8
|
+
? 10 * weightKg + 6.25 * heightCm - 5 * age + 5
|
|
9
|
+
: 10 * weightKg + 6.25 * heightCm - 5 * age - 161;
|
|
10
|
+
}
|
|
11
|
+
function calculateTDEE(profile) {
|
|
12
|
+
return calculateBMR(profile) * profile.activityLevel;
|
|
13
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateDailyMealPlan = generateDailyMealPlan;
|
|
4
|
+
const foodConversion_1 = require("./foodConversion");
|
|
5
|
+
// Mock food data - in a real app, this would come from Open Food Facts search
|
|
6
|
+
const mockFoods = {
|
|
7
|
+
// Breakfast foods
|
|
8
|
+
oatmeal: {
|
|
9
|
+
id: 'oatmeal',
|
|
10
|
+
name: 'Oatmeal',
|
|
11
|
+
brand: 'Generic',
|
|
12
|
+
nutritionPer100g: { calories: 379, protein: 13.2, carbs: 66.3, fat: 6.9 },
|
|
13
|
+
servingSize: 40,
|
|
14
|
+
unit: 'g'
|
|
15
|
+
},
|
|
16
|
+
banana: {
|
|
17
|
+
id: 'banana',
|
|
18
|
+
name: 'Banana',
|
|
19
|
+
brand: 'Generic',
|
|
20
|
+
nutritionPer100g: { calories: 89, protein: 1.1, carbs: 22.8, fat: 0.3 },
|
|
21
|
+
servingSize: 118,
|
|
22
|
+
unit: 'g'
|
|
23
|
+
},
|
|
24
|
+
milk: {
|
|
25
|
+
id: 'milk',
|
|
26
|
+
name: 'Whole Milk',
|
|
27
|
+
brand: 'Generic',
|
|
28
|
+
nutritionPer100g: { calories: 61, protein: 3.2, carbs: 4.8, fat: 3.3 },
|
|
29
|
+
servingSize: 244,
|
|
30
|
+
unit: 'ml'
|
|
31
|
+
},
|
|
32
|
+
// Lunch/Dinner proteins
|
|
33
|
+
chicken: {
|
|
34
|
+
id: 'chicken',
|
|
35
|
+
name: 'Chicken Breast',
|
|
36
|
+
brand: 'Generic',
|
|
37
|
+
nutritionPer100g: { calories: 165, protein: 31, carbs: 0, fat: 3.6 },
|
|
38
|
+
servingSize: 100,
|
|
39
|
+
unit: 'g'
|
|
40
|
+
},
|
|
41
|
+
salmon: {
|
|
42
|
+
id: 'salmon',
|
|
43
|
+
name: 'Salmon',
|
|
44
|
+
brand: 'Generic',
|
|
45
|
+
nutritionPer100g: { calories: 206, protein: 22, carbs: 0, fat: 13 },
|
|
46
|
+
servingSize: 100,
|
|
47
|
+
unit: 'g'
|
|
48
|
+
},
|
|
49
|
+
// Carbs
|
|
50
|
+
rice: {
|
|
51
|
+
id: 'rice',
|
|
52
|
+
name: 'White Rice',
|
|
53
|
+
brand: 'Generic',
|
|
54
|
+
nutritionPer100g: { calories: 130, protein: 2.7, carbs: 28, fat: 0.3 },
|
|
55
|
+
servingSize: 185,
|
|
56
|
+
unit: 'g'
|
|
57
|
+
},
|
|
58
|
+
bread: {
|
|
59
|
+
id: 'bread',
|
|
60
|
+
name: 'Whole Wheat Bread',
|
|
61
|
+
brand: 'Generic',
|
|
62
|
+
nutritionPer100g: { calories: 247, protein: 13, carbs: 41, fat: 3.5 },
|
|
63
|
+
servingSize: 45,
|
|
64
|
+
unit: 'g'
|
|
65
|
+
},
|
|
66
|
+
// Veggies
|
|
67
|
+
broccoli: {
|
|
68
|
+
id: 'broccoli',
|
|
69
|
+
name: 'Broccoli',
|
|
70
|
+
brand: 'Generic',
|
|
71
|
+
nutritionPer100g: { calories: 34, protein: 2.8, carbs: 7, fat: 0.4 },
|
|
72
|
+
servingSize: 91,
|
|
73
|
+
unit: 'g'
|
|
74
|
+
},
|
|
75
|
+
spinach: {
|
|
76
|
+
id: 'spinach',
|
|
77
|
+
name: 'Spinach',
|
|
78
|
+
brand: 'Generic',
|
|
79
|
+
nutritionPer100g: { calories: 23, protein: 2.9, carbs: 3.6, fat: 0.4 },
|
|
80
|
+
servingSize: 30,
|
|
81
|
+
unit: 'g'
|
|
82
|
+
},
|
|
83
|
+
// Snacks
|
|
84
|
+
apple: {
|
|
85
|
+
id: 'apple',
|
|
86
|
+
name: 'Apple',
|
|
87
|
+
brand: 'Generic',
|
|
88
|
+
nutritionPer100g: { calories: 52, protein: 0.3, carbs: 13.8, fat: 0.2 },
|
|
89
|
+
servingSize: 182,
|
|
90
|
+
unit: 'g'
|
|
91
|
+
},
|
|
92
|
+
almonds: {
|
|
93
|
+
id: 'almonds',
|
|
94
|
+
name: 'Almonds',
|
|
95
|
+
brand: 'Generic',
|
|
96
|
+
nutritionPer100g: { calories: 579, protein: 21.2, carbs: 21.6, fat: 49.9 },
|
|
97
|
+
servingSize: 28,
|
|
98
|
+
unit: 'g'
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
function calculateDailyNeeds(profile) {
|
|
102
|
+
// Provide defaults for missing profile data
|
|
103
|
+
const age = profile.age || 30;
|
|
104
|
+
const height = profile.height || 170; // cm
|
|
105
|
+
const weight = profile.weight || 70; // kg
|
|
106
|
+
const gender = profile.gender || 'male';
|
|
107
|
+
const activityLevel = profile.activityLevel || 'moderately_active';
|
|
108
|
+
// Basic BMR calculation (simplified)
|
|
109
|
+
const bmr = gender === 'male'
|
|
110
|
+
? 88.362 + (13.397 * weight) + (4.799 * height) - (5.677 * age)
|
|
111
|
+
: 447.593 + (9.247 * weight) + (3.098 * height) - (4.330 * age);
|
|
112
|
+
// Activity multiplier
|
|
113
|
+
const activityMultipliers = {
|
|
114
|
+
sedentary: 1.2,
|
|
115
|
+
lightly_active: 1.375,
|
|
116
|
+
moderately_active: 1.55,
|
|
117
|
+
very_active: 1.725,
|
|
118
|
+
extremely_active: 1.9
|
|
119
|
+
};
|
|
120
|
+
const tdee = bmr * (activityMultipliers[activityLevel] || 1.2);
|
|
121
|
+
// Adjust based on goals
|
|
122
|
+
let dailyCalories = tdee;
|
|
123
|
+
if (profile.healthGoals?.includes('weight_loss')) {
|
|
124
|
+
dailyCalories -= 500;
|
|
125
|
+
}
|
|
126
|
+
else if (profile.healthGoals?.includes('weight_gain')) {
|
|
127
|
+
dailyCalories += 500;
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
calories: Math.round(Math.max(dailyCalories, 1200)), // Minimum 1200 calories
|
|
131
|
+
protein: Math.round(Math.max(dailyCalories * 0.3 / 4, 50)), // Minimum 50g protein
|
|
132
|
+
carbs: Math.round(Math.max(dailyCalories * 0.4 / 4, 100)), // Minimum 100g carbs
|
|
133
|
+
fat: Math.round(Math.max(dailyCalories * 0.3 / 9, 30)) // Minimum 30g fat
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function createMeal(name, type, foods) {
|
|
137
|
+
const ingredients = foods.map(({ food, amount, unit }) => (0, foodConversion_1.convertFoodItemToIngredient)(food, amount, unit));
|
|
138
|
+
const meal = (0, foodConversion_1.createMealFromFoodItems)(name, type, foods.map(({ food, amount, unit }, index) => ({
|
|
139
|
+
food: food,
|
|
140
|
+
amount,
|
|
141
|
+
unit
|
|
142
|
+
})));
|
|
143
|
+
return {
|
|
144
|
+
...meal,
|
|
145
|
+
instructions: [`Prepare ${name} according to recipe`],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function generateDailyMealPlan(profile, userId) {
|
|
149
|
+
const needs = calculateDailyNeeds(profile);
|
|
150
|
+
// Check dietary restrictions
|
|
151
|
+
const isVegetarian = profile.dietaryRestrictions?.includes('vegetarian');
|
|
152
|
+
const isVegan = profile.dietaryRestrictions?.includes('vegan');
|
|
153
|
+
// Breakfast: Oatmeal with fruit
|
|
154
|
+
const breakfast = createMeal('Healthy Oatmeal Breakfast', 'breakfast', [
|
|
155
|
+
{ food: mockFoods.oatmeal, amount: 50, unit: 'g' },
|
|
156
|
+
{ food: mockFoods.banana, amount: 100, unit: 'g' },
|
|
157
|
+
{ food: mockFoods.milk, amount: 200, unit: 'ml' }
|
|
158
|
+
]);
|
|
159
|
+
// Lunch: Chicken salad (or vegetarian alternative)
|
|
160
|
+
const lunchProtein = isVegan ? mockFoods.broccoli : (isVegetarian ? mockFoods.broccoli : mockFoods.chicken);
|
|
161
|
+
const lunch = createMeal('Nutritious Lunch Salad', 'lunch', [
|
|
162
|
+
{ food: lunchProtein, amount: lunchProtein === mockFoods.chicken ? 150 : 200, unit: 'g' },
|
|
163
|
+
{ food: mockFoods.spinach, amount: 100, unit: 'g' },
|
|
164
|
+
{ food: mockFoods.broccoli, amount: 150, unit: 'g' },
|
|
165
|
+
{ food: mockFoods.bread, amount: 90, unit: 'g' }
|
|
166
|
+
]);
|
|
167
|
+
// Dinner: Salmon with rice and veggies (or vegetarian alternative)
|
|
168
|
+
const dinnerProtein = isVegan ? mockFoods.broccoli : (isVegetarian ? mockFoods.broccoli : mockFoods.salmon);
|
|
169
|
+
const dinner = createMeal('Balanced Dinner', 'dinner', [
|
|
170
|
+
{ food: dinnerProtein, amount: dinnerProtein === mockFoods.salmon ? 150 : 200, unit: 'g' },
|
|
171
|
+
{ food: mockFoods.rice, amount: 150, unit: 'g' },
|
|
172
|
+
{ food: mockFoods.broccoli, amount: 100, unit: 'g' }
|
|
173
|
+
]);
|
|
174
|
+
// Snack: Apple and almonds
|
|
175
|
+
const snack = createMeal('Healthy Snack', 'snack', [
|
|
176
|
+
{ food: mockFoods.apple, amount: 150, unit: 'g' },
|
|
177
|
+
{ food: mockFoods.almonds, amount: 20, unit: 'g' }
|
|
178
|
+
]);
|
|
179
|
+
const meals = [breakfast, lunch, dinner, snack];
|
|
180
|
+
const totalNutrition = meals.reduce((total, meal) => ({
|
|
181
|
+
calories: total.calories + (meal.nutrition?.calories || 0),
|
|
182
|
+
protein: Math.round((total.protein + (meal.nutrition?.protein || 0)) * 10) / 10,
|
|
183
|
+
carbs: total.carbs + (meal.nutrition?.carbs || 0),
|
|
184
|
+
fat: total.fat + (meal.nutrition?.fat || 0)
|
|
185
|
+
}), { calories: 0, protein: 0, carbs: 0, fat: 0 });
|
|
186
|
+
const today = new Date();
|
|
187
|
+
return {
|
|
188
|
+
id: `daily-plan-${Date.now()}`,
|
|
189
|
+
userId: userId,
|
|
190
|
+
name: `Daily Meal Plan - ${today.toLocaleDateString()}`,
|
|
191
|
+
description: `Personalized daily meal plan based on your profile (${Math.round(totalNutrition.calories)} cal)`,
|
|
192
|
+
startDate: today.toISOString().split('T')[0],
|
|
193
|
+
endDate: today.toISOString().split('T')[0],
|
|
194
|
+
dailyPlans: [],
|
|
195
|
+
meals,
|
|
196
|
+
totalCalories: totalNutrition.calories,
|
|
197
|
+
totalProtein: totalNutrition.protein,
|
|
198
|
+
totalCarbs: totalNutrition.carbs,
|
|
199
|
+
totalFat: totalNutrition.fat,
|
|
200
|
+
uid: userId,
|
|
201
|
+
goals: {
|
|
202
|
+
dailyCalories: Math.round(totalNutrition.calories),
|
|
203
|
+
proteinPercentage: 25,
|
|
204
|
+
carbsPercentage: 45,
|
|
205
|
+
fatPercentage: 30,
|
|
206
|
+
},
|
|
207
|
+
createdAt: new Date(),
|
|
208
|
+
updatedAt: new Date()
|
|
209
|
+
};
|
|
210
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { NormalizedFoodItem } from './types/openfoodfacts';
|
|
2
|
+
import { Meal, Ingredient } from './types/firestore';
|
|
3
|
+
export interface ExtendedIngredient extends Ingredient {
|
|
4
|
+
barcode?: string;
|
|
5
|
+
brand?: string;
|
|
6
|
+
nutritionGrade?: string;
|
|
7
|
+
ecoScore?: string;
|
|
8
|
+
allergens?: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Convert Open Food Facts item to Ingredient for meal planning
|
|
12
|
+
*/
|
|
13
|
+
export declare function convertFoodItemToIngredient(foodItem: NormalizedFoodItem, amount?: number, unit?: string): Ingredient;
|
|
14
|
+
/**
|
|
15
|
+
* Calculate total nutrition for a list of ingredients
|
|
16
|
+
*/
|
|
17
|
+
export declare function calculateTotalNutrition(ingredients: Ingredient[]): {
|
|
18
|
+
calories: number;
|
|
19
|
+
protein: number;
|
|
20
|
+
carbs: number;
|
|
21
|
+
fat: number;
|
|
22
|
+
fiber: number;
|
|
23
|
+
sugar: number;
|
|
24
|
+
sodium: number;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Create a meal from selected food items
|
|
28
|
+
*/
|
|
29
|
+
export declare function createMealFromFoodItems(name: string, type: 'breakfast' | 'lunch' | 'dinner' | 'snack', foodItems: Array<{
|
|
30
|
+
food: NormalizedFoodItem;
|
|
31
|
+
amount: number;
|
|
32
|
+
unit: string;
|
|
33
|
+
}>): Meal;
|
|
34
|
+
/**
|
|
35
|
+
* Get nutrition density score (protein per calorie ratio)
|
|
36
|
+
*/
|
|
37
|
+
export declare function getNutritionDensityScore(foodItem: NormalizedFoodItem): number;
|
|
38
|
+
/**
|
|
39
|
+
* Check if food item meets dietary restrictions
|
|
40
|
+
*/
|
|
41
|
+
export declare function checkDietaryRestrictions(foodItem: NormalizedFoodItem, restrictions: string[]): {
|
|
42
|
+
meets: boolean;
|
|
43
|
+
violations: string[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Get recommended serving size based on meal type and nutrition goals
|
|
47
|
+
*/
|
|
48
|
+
export declare function getRecommendedServingSize(foodItem: NormalizedFoodItem, mealType: 'breakfast' | 'lunch' | 'dinner' | 'snack', targetCalories?: number): {
|
|
49
|
+
amount: number;
|
|
50
|
+
unit: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Filter foods by health goals
|
|
54
|
+
*/
|
|
55
|
+
export declare function filterFoodsByHealthGoals(foods: NormalizedFoodItem[], healthGoals: string[]): NormalizedFoodItem[];
|