feat: full health & fitness app with workout/meal planning
Some checks failed
CI/CD - Build, Push & Deploy / Build & Push Docker Image (push) Has been cancelled
CI/CD - Build, Push & Deploy / Update GitOps Manifest (push) Has been cancelled

- Login/register with JWT auth
- User profile (age, weight, height, goal, country)
- AI trainer agent: 7-day workout programs by goal
- AI dietitian agent: calorie-based meal plans with Turkish cuisine
- Shopping list generator from meal plans
- Modern Turkish UI (SPA)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 10:02:46 +00:00
parent d0d40c2195
commit c17f143a40
13 changed files with 3318 additions and 18 deletions

48
src/database.js Normal file
View File

@@ -0,0 +1,48 @@
"use strict";
const Database = require("better-sqlite3");
const path = require("path");
const DB_PATH = process.env.DB_PATH || "/tmp/health-app/health-app.db";
let db;
function getDb() {
if (!db) {
db = new Database(DB_PATH);
db.pragma("journal_mode = WAL");
db.pragma("foreign_keys = ON");
initTables();
}
return db;
}
function initTables() {
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
name TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS profiles (
user_id INTEGER PRIMARY KEY,
age INTEGER,
gender TEXT,
height REAL,
weight REAL,
activity_level TEXT DEFAULT 'moderate',
country TEXT DEFAULT 'Turkey',
city TEXT DEFAULT '',
goal TEXT DEFAULT 'maintain',
allergies TEXT DEFAULT '',
dietary_restrictions TEXT DEFAULT '',
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
`);
}
module.exports = { getDb };

24
src/middleware/auth.js Normal file
View File

@@ -0,0 +1,24 @@
"use strict";
const jwt = require("jsonwebtoken");
const JWT_SECRET = process.env.JWT_SECRET || "health-app-default-secret-change-in-production";
function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.startsWith("Bearer ") ? authHeader.slice(7) : null;
if (!token) {
return res.status(401).json({ error: "Yetkilendirme token'ı gerekli" });
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
return res.status(403).json({ error: "Geçersiz veya süresi dolmuş token" });
}
}
module.exports = { authenticateToken, JWT_SECRET };

1142
src/public/index.html Normal file

File diff suppressed because it is too large Load Diff

92
src/routes/auth.js Normal file
View File

@@ -0,0 +1,92 @@
"use strict";
const express = require("express");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const { getDb } = require("../database");
const { authenticateToken, JWT_SECRET } = require("../middleware/auth");
const router = express.Router();
// POST /api/auth/register
router.post("/register", (req, res) => {
try {
const { email, password, name } = req.body;
if (!email || !password || !name) {
return res.status(400).json({ error: "Email, şifre ve isim gereklidir" });
}
if (password.length < 6) {
return res.status(400).json({ error: "Şifre en az 6 karakter olmalıdır" });
}
const db = getDb();
const existing = db.prepare("SELECT id FROM users WHERE email = ?").get(email);
if (existing) {
return res.status(409).json({ error: "Bu email adresi zaten kayıtlı" });
}
const passwordHash = bcrypt.hashSync(password, 10);
const result = db.prepare("INSERT INTO users (email, password_hash, name) VALUES (?, ?, ?)").run(email, passwordHash, name);
const token = jwt.sign({ id: result.lastInsertRowid, email, name }, JWT_SECRET, { expiresIn: "7d" });
res.status(201).json({
message: "Kayıt başarılı",
token,
user: { id: result.lastInsertRowid, email, name },
});
} catch (err) {
console.error("Register error:", err.message);
res.status(500).json({ error: "Sunucu hatası" });
}
});
// POST /api/auth/login
router.post("/login", (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ error: "Email ve şifre gereklidir" });
}
const db = getDb();
const user = db.prepare("SELECT * FROM users WHERE email = ?").get(email);
if (!user || !bcrypt.compareSync(password, user.password_hash)) {
return res.status(401).json({ error: "Geçersiz email veya şifre" });
}
const token = jwt.sign({ id: user.id, email: user.email, name: user.name }, JWT_SECRET, { expiresIn: "7d" });
res.json({
message: "Giriş başarılı",
token,
user: { id: user.id, email: user.email, name: user.name },
});
} catch (err) {
console.error("Login error:", err.message);
res.status(500).json({ error: "Sunucu hatası" });
}
});
// GET /api/auth/me
router.get("/me", authenticateToken, (req, res) => {
try {
const db = getDb();
const user = db.prepare("SELECT id, email, name, created_at FROM users WHERE id = ?").get(req.user.id);
if (!user) {
return res.status(404).json({ error: "Kullanıcı bulunamadı" });
}
res.json({ user });
} catch (err) {
console.error("Me error:", err.message);
res.status(500).json({ error: "Sunucu hatası" });
}
});
module.exports = router;

72
src/routes/profile.js Normal file
View File

@@ -0,0 +1,72 @@
"use strict";
const express = require("express");
const { getDb } = require("../database");
const { authenticateToken } = require("../middleware/auth");
const router = express.Router();
// GET /api/profile
router.get("/", authenticateToken, (req, res) => {
try {
const db = getDb();
const profile = db.prepare("SELECT * FROM profiles WHERE user_id = ?").get(req.user.id);
if (!profile) {
return res.json({ profile: null });
}
res.json({ profile });
} catch (err) {
console.error("Get profile error:", err.message);
res.status(500).json({ error: "Sunucu hatası" });
}
});
// POST /api/profile
router.post("/", authenticateToken, (req, res) => {
try {
const { age, gender, height, weight, activity_level, country, city, goal, allergies, dietary_restrictions } = req.body;
if (!age || !gender || !height || !weight) {
return res.status(400).json({ error: "Yaş, cinsiyet, boy ve kilo gereklidir" });
}
const validGenders = ["male", "female"];
const validActivityLevels = ["sedentary", "light", "moderate", "active", "very_active"];
const validGoals = ["lose_weight", "gain_weight", "build_muscle", "maintain"];
if (!validGenders.includes(gender)) {
return res.status(400).json({ error: "Geçersiz cinsiyet değeri" });
}
if (activity_level && !validActivityLevels.includes(activity_level)) {
return res.status(400).json({ error: "Geçersiz aktivite seviyesi" });
}
if (goal && !validGoals.includes(goal)) {
return res.status(400).json({ error: "Geçersiz hedef" });
}
const db = getDb();
const existing = db.prepare("SELECT user_id FROM profiles WHERE user_id = ?").get(req.user.id);
if (existing) {
db.prepare(`
UPDATE profiles SET age=?, gender=?, height=?, weight=?, activity_level=?, country=?, city=?, goal=?, allergies=?, dietary_restrictions=?, updated_at=datetime('now')
WHERE user_id=?
`).run(age, gender, height, weight, activity_level || "moderate", country || "Turkey", city || "", goal || "maintain", allergies || "", dietary_restrictions || "", req.user.id);
} else {
db.prepare(`
INSERT INTO profiles (user_id, age, gender, height, weight, activity_level, country, city, goal, allergies, dietary_restrictions)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(req.user.id, age, gender, height, weight, activity_level || "moderate", country || "Turkey", city || "", goal || "maintain", allergies || "", dietary_restrictions || "");
}
const profile = db.prepare("SELECT * FROM profiles WHERE user_id = ?").get(req.user.id);
res.json({ message: "Profil güncellendi", profile });
} catch (err) {
console.error("Save profile error:", err.message);
res.status(500).json({ error: "Sunucu hatası" });
}
});
module.exports = router;

63
src/routes/program.js Normal file
View File

@@ -0,0 +1,63 @@
"use strict";
const express = require("express");
const { getDb } = require("../database");
const { authenticateToken } = require("../middleware/auth");
const { generateWorkoutProgram } = require("../services/trainer");
const { generateMealPlan } = require("../services/dietitian");
const { generateShoppingList } = require("../services/shopping");
const router = express.Router();
function getProfile(userId) {
const db = getDb();
return db.prepare("SELECT * FROM profiles WHERE user_id = ?").get(userId);
}
// GET /api/program/workout
router.get("/workout", authenticateToken, (req, res) => {
try {
const profile = getProfile(req.user.id);
if (!profile) {
return res.status(400).json({ error: "Lütfen önce profilinizi oluşturun" });
}
const program = generateWorkoutProgram(profile);
res.json(program);
} catch (err) {
console.error("Workout program error:", err.message);
res.status(500).json({ error: "Program oluşturulurken hata oluştu" });
}
});
// GET /api/program/meal
router.get("/meal", authenticateToken, (req, res) => {
try {
const profile = getProfile(req.user.id);
if (!profile) {
return res.status(400).json({ error: "Lütfen önce profilinizi oluşturun" });
}
const plan = generateMealPlan(profile);
res.json(plan);
} catch (err) {
console.error("Meal plan error:", err.message);
res.status(500).json({ error: "Beslenme planı oluşturulurken hata oluştu" });
}
});
// GET /api/program/shopping
router.get("/shopping", authenticateToken, (req, res) => {
try {
const profile = getProfile(req.user.id);
if (!profile) {
return res.status(400).json({ error: "Lütfen önce profilinizi oluşturun" });
}
const mealPlan = generateMealPlan(profile);
const shoppingList = generateShoppingList(mealPlan);
res.json(shoppingList);
} catch (err) {
console.error("Shopping list error:", err.message);
res.status(500).json({ error: "Alışveriş listesi oluşturulurken hata oluştu" });
}
});
module.exports = router;

View File

@@ -1,6 +1,12 @@
"use strict";
const express = require("express");
const path = require("path");
const { getDb } = require("./database");
const authRoutes = require("./routes/auth");
const profileRoutes = require("./routes/profile");
const programRoutes = require("./routes/program");
const app = express();
const PORT = parseInt(process.env.PORT, 10) || 3000;
@@ -8,14 +14,17 @@ const startTime = Date.now();
app.disable("x-powered-by");
app.get("/", (_req, res) => {
res.json({
service: "health-app",
version: process.env.APP_VERSION || "1.0.0",
environment: process.env.NODE_ENV || "development",
});
});
// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Static files
app.use(express.static(path.join(__dirname, "public")));
// Initialize database on startup
getDb();
// Health & readiness probes (Kubernetes)
app.get("/health", (_req, res) => {
res.json({
status: "healthy",
@@ -26,16 +35,36 @@ app.get("/health", (_req, res) => {
});
app.get("/ready", (_req, res) => {
res.json({ ready: true });
try {
getDb();
res.json({ ready: true });
} catch (err) {
res.status(503).json({ ready: false, error: err.message });
}
});
app.use((_req, res) => {
res.status(404).json({ error: "Not Found" });
// API routes
app.use("/api/auth", authRoutes);
app.use("/api/profile", profileRoutes);
app.use("/api/program", programRoutes);
// SPA fallback - serve index.html for non-API routes
app.get("*", (req, res) => {
if (req.path.startsWith("/api/")) {
return res.status(404).json({ error: "API endpoint bulunamadı" });
}
res.sendFile(path.join(__dirname, "public", "index.html"));
});
// 404 handler for API
app.use((req, res) => {
res.status(404).json({ error: "Bulunamadı" });
});
// Error handler
app.use((err, _req, res, _next) => {
console.error("Unhandled error:", err.message);
res.status(500).json({ error: "Internal Server Error" });
res.status(500).json({ error: "Sunucu hatası" });
});
const server = app.listen(PORT, "0.0.0.0", () => {

754
src/services/dietitian.js Normal file
View File

@@ -0,0 +1,754 @@
"use strict";
// Mifflin-St Jeor BMR
function calculateBMR(weight, height, age, gender) {
if (gender === "male") {
return 10 * weight + 6.25 * height - 5 * age + 5;
}
return 10 * weight + 6.25 * height - 5 * age - 161;
}
const ACTIVITY_MULTIPLIERS = {
sedentary: 1.2,
light: 1.375,
moderate: 1.55,
active: 1.725,
very_active: 1.9,
};
function calculateTDEE(bmr, activityLevel) {
return bmr * (ACTIVITY_MULTIPLIERS[activityLevel] || 1.55);
}
function adjustCalories(tdee, goal) {
switch (goal) {
case "lose_weight": return tdee - 500;
case "gain_weight": return tdee + 500;
case "build_muscle": return tdee + 300;
default: return tdee;
}
}
function calculateMacros(calories, goal) {
let proteinPct, carbsPct, fatPct;
switch (goal) {
case "lose_weight":
proteinPct = 0.35; carbsPct = 0.35; fatPct = 0.30; break;
case "build_muscle":
proteinPct = 0.35; carbsPct = 0.40; fatPct = 0.25; break;
case "gain_weight":
proteinPct = 0.30; carbsPct = 0.45; fatPct = 0.25; break;
default:
proteinPct = 0.30; carbsPct = 0.40; fatPct = 0.30;
}
return {
protein_g: Math.round((calories * proteinPct) / 4),
carbs_g: Math.round((calories * carbsPct) / 4),
fat_g: Math.round((calories * fatPct) / 9),
};
}
// ==================== TURKISH MEALS ====================
const TURKISH_BREAKFASTS = [
{
name: "Menemen & Simit Kahvaltısı",
ingredients: [
{ item: "Yumurta", amount: "2 adet", grams: 120 },
{ item: "Domates", amount: "1 adet", grams: 100 },
{ item: "Biber (sivri)", amount: "2 adet", grams: 60 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Simit", amount: "1/2 adet", grams: 60 },
{ item: "Beyaz peynir", amount: "40g", grams: 40 },
],
calories: 420, protein: 22, carbs: 35, fat: 22,
},
{
name: "Peynirli Kahvaltı Tabağı",
ingredients: [
{ item: "Beyaz peynir", amount: "60g", grams: 60 },
{ item: "Kaşar peyniri", amount: "30g", grams: 30 },
{ item: "Zeytin (siyah)", amount: "8 adet", grams: 30 },
{ item: "Domates", amount: "1 adet", grams: 120 },
{ item: "Salatalık", amount: "1 adet", grams: 100 },
{ item: "Bal", amount: "1 tatlı kaşığı", grams: 10 },
{ item: "Tam buğday ekmek", amount: "2 dilim", grams: 60 },
{ item: "Tereyağı", amount: "10g", grams: 10 },
],
calories: 450, protein: 20, carbs: 38, fat: 24,
},
{
name: "Sucuklu Yumurta",
ingredients: [
{ item: "Yumurta", amount: "2 adet", grams: 120 },
{ item: "Sucuk", amount: "40g", grams: 40 },
{ item: "Tam buğday ekmek", amount: "1 dilim", grams: 30 },
{ item: "Domates", amount: "1/2 adet", grams: 60 },
{ item: "Yeşil biber", amount: "1 adet", grams: 30 },
],
calories: 400, protein: 24, carbs: 18, fat: 26,
},
{
name: "Kaymak & Bal ile Simit",
ingredients: [
{ item: "Simit", amount: "1 adet", grams: 120 },
{ item: "Kaymak", amount: "30g", grams: 30 },
{ item: "Bal", amount: "1 yemek kaşığı", grams: 20 },
{ item: "Çay", amount: "1 bardak", grams: 200 },
{ item: "Beyaz peynir", amount: "30g", grams: 30 },
],
calories: 460, protein: 14, carbs: 55, fat: 20,
},
{
name: "Sahanda Yumurta & Peynir",
ingredients: [
{ item: "Yumurta", amount: "2 adet", grams: 120 },
{ item: "Tereyağı", amount: "10g", grams: 10 },
{ item: "Beyaz peynir", amount: "40g", grams: 40 },
{ item: "Domates", amount: "1 adet", grams: 100 },
{ item: "Zeytin", amount: "6 adet", grams: 24 },
{ item: "Tam buğday ekmek", amount: "1 dilim", grams: 30 },
],
calories: 410, protein: 23, carbs: 20, fat: 27,
},
{
name: "Gözleme (Peynirli & Ispanaklı)",
ingredients: [
{ item: "Un (hamur)", amount: "80g", grams: 80 },
{ item: "Ispanak", amount: "60g", grams: 60 },
{ item: "Beyaz peynir", amount: "50g", grams: 50 },
{ item: "Tereyağı", amount: "10g", grams: 10 },
{ item: "Ayran", amount: "1 bardak", grams: 200 },
],
calories: 430, protein: 20, carbs: 42, fat: 20,
},
{
name: ılbır (Yumurtalı Yoğurt)",
ingredients: [
{ item: "Yumurta", amount: "2 adet", grams: 120 },
{ item: "Yoğurt", amount: "150g", grams: 150 },
{ item: "Tereyağı", amount: "10g", grams: 10 },
{ item: "Pul biber", amount: "1 çay kaşığı", grams: 2 },
{ item: "Tam buğday ekmek", amount: "1 dilim", grams: 30 },
],
calories: 380, protein: 24, carbs: 22, fat: 22,
},
];
const TURKISH_LUNCHES = [
{
name: "Mercimek Çorbası & Bulgur Pilavı",
ingredients: [
{ item: "Kırmızı mercimek", amount: "80g", grams: 80 },
{ item: "Soğan", amount: "1/2 adet", grams: 40 },
{ item: "Havuç", amount: "1/2 adet", grams: 40 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Limon", amount: "1/2 adet", grams: 30 },
{ item: "Bulgur pilavı", amount: "150g (pişmiş)", grams: 150 },
{ item: "Tereyağı", amount: "5g", grams: 5 },
],
calories: 480, protein: 22, carbs: 68, fat: 14,
},
{
name: "Izgara Köfte & Salata",
ingredients: [
{ item: "Dana kıyma (az yağlı)", amount: "150g", grams: 150 },
{ item: "Soğan (rendelenmiş)", amount: "30g", grams: 30 },
{ item: "Maydanoz", amount: "10g", grams: 10 },
{ item: "Çoban salatası", amount: "150g", grams: 150 },
{ item: "Tam buğday ekmek", amount: "1 dilim", grams: 30 },
{ item: "Sumak", amount: "1 çay kaşığı", grams: 2 },
],
calories: 450, protein: 38, carbs: 22, fat: 24,
},
{
name: "Tavuk Şiş & Bulgur",
ingredients: [
{ item: "Tavuk göğsü", amount: "180g", grams: 180 },
{ item: "Biber", amount: "2 adet", grams: 60 },
{ item: "Domates", amount: "1 adet", grams: 100 },
{ item: "Bulgur pilavı", amount: "150g (pişmiş)", grams: 150 },
{ item: "Zeytinyağı", amount: "1 tatlı kaşığı", grams: 7 },
{ item: "Soğan", amount: "1/4 adet", grams: 20 },
],
calories: 480, protein: 45, carbs: 42, fat: 14,
},
{
name: "Kuru Fasulye & Pilav",
ingredients: [
{ item: "Kuru fasulye (pişmiş)", amount: "200g", grams: 200 },
{ item: "Domates salçası", amount: "1 yemek kaşığı", grams: 15 },
{ item: "Soğan", amount: "1/2 adet", grams: 40 },
{ item: "Tereyağlı pirinç pilavı", amount: "150g (pişmiş)", grams: 150 },
{ item: "Turşu", amount: "50g", grams: 50 },
],
calories: 520, protein: 24, carbs: 78, fat: 12,
},
{
name: "Patlıcan Musakka",
ingredients: [
{ item: "Patlıcan", amount: "200g", grams: 200 },
{ item: "Dana kıyma", amount: "100g", grams: 100 },
{ item: "Domates", amount: "1 adet", grams: 100 },
{ item: "Biber", amount: "1 adet", grams: 30 },
{ item: "Soğan", amount: "1/2 adet", grams: 40 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Pilav", amount: "100g (pişmiş)", grams: 100 },
],
calories: 470, protein: 28, carbs: 35, fat: 24,
},
{
name: "Karnıyarık",
ingredients: [
{ item: "Patlıcan", amount: "250g", grams: 250 },
{ item: "Dana kıyma", amount: "100g", grams: 100 },
{ item: "Domates", amount: "1 adet", grams: 100 },
{ item: "Soğan", amount: "1/2 adet", grams: 40 },
{ item: "Sarımsak", amount: "2 diş", grams: 6 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Bulgur pilavı", amount: "100g (pişmiş)", grams: 100 },
],
calories: 490, protein: 28, carbs: 38, fat: 25,
},
{
name: "İmam Bayıldı & Pilav",
ingredients: [
{ item: "Patlıcan", amount: "250g", grams: 250 },
{ item: "Domates", amount: "2 adet", grams: 200 },
{ item: "Soğan", amount: "1 adet", grams: 80 },
{ item: "Sarımsak", amount: "3 diş", grams: 9 },
{ item: "Zeytinyağı", amount: "2 yemek kaşığı", grams: 28 },
{ item: "Pilav", amount: "120g (pişmiş)", grams: 120 },
],
calories: 440, protein: 10, carbs: 48, fat: 24,
},
{
name: "Lahmacun & Ayran",
ingredients: [
{ item: "Lahmacun", amount: "2 adet", grams: 200 },
{ item: "Maydanoz", amount: "15g", grams: 15 },
{ item: "Limon", amount: "1/2 adet", grams: 30 },
{ item: "Domates", amount: "1/2 adet", grams: 60 },
{ item: "Ayran", amount: "1 bardak", grams: 200 },
],
calories: 460, protein: 24, carbs: 52, fat: 16,
},
{
name: "Etli Pide",
ingredients: [
{ item: "Pide hamuru", amount: "150g", grams: 150 },
{ item: "Dana kıyma", amount: "100g", grams: 100 },
{ item: "Domates", amount: "1/2 adet", grams: 60 },
{ item: "Biber", amount: "1 adet", grams: 30 },
{ item: "Soğan", amount: "1/4 adet", grams: 20 },
{ item: "Ayran", amount: "1 bardak", grams: 200 },
],
calories: 530, protein: 30, carbs: 52, fat: 22,
},
{
name: "Ezogelin Çorbası & Tavuk Sote",
ingredients: [
{ item: "Kırmızı mercimek", amount: "50g", grams: 50 },
{ item: "Bulgur", amount: "20g", grams: 20 },
{ item: "Domates salçası", amount: "1 yemek kaşığı", grams: 15 },
{ item: "Tavuk göğsü", amount: "150g", grams: 150 },
{ item: "Biber", amount: "2 adet", grams: 60 },
{ item: "Domates", amount: "1 adet", grams: 100 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
],
calories: 460, protein: 40, carbs: 36, fat: 16,
},
{
name: "Yayla Çorbası & Mantı",
ingredients: [
{ item: "Yoğurt", amount: "150g", grams: 150 },
{ item: "Pirinç", amount: "30g", grams: 30 },
{ item: "Yumurta", amount: "1 adet", grams: 60 },
{ item: "Nane (kuru)", amount: "1 çay kaşığı", grams: 1 },
{ item: "Mantı (dondurulmuş veya ev yapımı)", amount: "150g", grams: 150 },
{ item: "Tereyağı", amount: "10g", grams: 10 },
],
calories: 500, protein: 26, carbs: 54, fat: 20,
},
{
name: "Taze Fasulye Yemeği & Pilav",
ingredients: [
{ item: "Taze fasulye", amount: "250g", grams: 250 },
{ item: "Domates", amount: "1 adet", grams: 100 },
{ item: "Soğan", amount: "1/2 adet", grams: 40 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Pilav", amount: "150g (pişmiş)", grams: 150 },
],
calories: 420, protein: 12, carbs: 58, fat: 16,
},
{
name: "Bamya Yemeği & Bulgur",
ingredients: [
{ item: "Bamya", amount: "200g", grams: 200 },
{ item: "Domates", amount: "1 adet", grams: 100 },
{ item: "Et (kuşbaşı)", amount: "100g", grams: 100 },
{ item: "Soğan", amount: "1/2 adet", grams: 40 },
{ item: "Limon suyu", amount: "1 yemek kaşığı", grams: 15 },
{ item: "Bulgur pilavı", amount: "120g (pişmiş)", grams: 120 },
],
calories: 440, protein: 30, carbs: 42, fat: 16,
},
{
name: "Adana Kebap & Şalgam",
ingredients: [
{ item: "Adana kebap", amount: "200g", grams: 200 },
{ item: "Lavaş ekmek", amount: "1 adet", grams: 60 },
{ item: "Közlenmiş domates", amount: "1 adet", grams: 100 },
{ item: "Közlenmiş biber", amount: "2 adet", grams: 60 },
{ item: "Soğan", amount: "1/4 adet", grams: 20 },
{ item: "Şalgam suyu", amount: "1 bardak", grams: 200 },
],
calories: 520, protein: 36, carbs: 30, fat: 28,
},
];
const TURKISH_DINNERS = [
{
name: "Zeytinyağlı Yaprak Sarma & Cacık",
ingredients: [
{ item: "Asma yaprağı sarması", amount: "8 adet", grams: 160 },
{ item: "Yoğurt (cacık)", amount: "150g", grams: 150 },
{ item: "Salatalık", amount: "1/2 adet", grams: 50 },
{ item: "Sarımsak", amount: "1 diş", grams: 3 },
{ item: "Nane", amount: "1 çay kaşığı", grams: 1 },
{ item: "Tam buğday ekmek", amount: "1 dilim", grams: 30 },
],
calories: 380, protein: 14, carbs: 48, fat: 14,
},
{
name: "Tavuk Izgara & Kısır",
ingredients: [
{ item: "Tavuk göğsü (ızgara)", amount: "180g", grams: 180 },
{ item: "Kısır (bulgur salatası)", amount: "150g", grams: 150 },
{ item: "Nar ekşisi", amount: "1 tatlı kaşığı", grams: 5 },
{ item: "Maydanoz", amount: "15g", grams: 15 },
{ item: "Domates", amount: "1/2 adet", grams: 60 },
],
calories: 440, protein: 44, carbs: 38, fat: 10,
},
{
name: "Kabak Mücver & Yoğurt",
ingredients: [
{ item: "Kabak (rendelenmiş)", amount: "200g", grams: 200 },
{ item: "Un", amount: "30g", grams: 30 },
{ item: "Yumurta", amount: "1 adet", grams: 60 },
{ item: "Dereotu", amount: "10g", grams: 10 },
{ item: "Beyaz peynir", amount: "30g", grams: 30 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Yoğurt", amount: "100g", grams: 100 },
],
calories: 360, protein: 18, carbs: 28, fat: 20,
},
{
name: "Su Böreği",
ingredients: [
{ item: "Yufka", amount: "3 yaprak", grams: 120 },
{ item: "Beyaz peynir", amount: "100g", grams: 100 },
{ item: "Maydanoz", amount: "15g", grams: 15 },
{ item: "Yumurta", amount: "1 adet", grams: 60 },
{ item: "Süt", amount: "50ml", grams: 50 },
{ item: "Tereyağı", amount: "15g", grams: 15 },
],
calories: 440, protein: 22, carbs: 38, fat: 22,
},
{
name: "Mercimek Köftesi & Salata",
ingredients: [
{ item: "Kırmızı mercimek", amount: "100g", grams: 100 },
{ item: "İnce bulgur", amount: "80g", grams: 80 },
{ item: "Soğan", amount: "1 adet", grams: 80 },
{ item: "Domates salçası", amount: "1 yemek kaşığı", grams: 15 },
{ item: "Marul", amount: "4 yaprak", grams: 40 },
{ item: "Limon", amount: "1/2 adet", grams: 30 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
],
calories: 400, protein: 20, carbs: 58, fat: 10,
},
{
name: "Sigara Böreği & Çorba",
ingredients: [
{ item: "Yufka (sigara böreği)", amount: "4 adet", grams: 120 },
{ item: "Beyaz peynir", amount: "60g", grams: 60 },
{ item: "Maydanoz", amount: "10g", grams: 10 },
{ item: "Zeytinyağı (kızartma)", amount: "15g", grams: 15 },
{ item: "Domates çorbası", amount: "250ml", grams: 250 },
],
calories: 420, protein: 18, carbs: 36, fat: 22,
},
{
name: "Çoban Salatası & Humus & Ekmek",
ingredients: [
{ item: "Domates", amount: "2 adet", grams: 200 },
{ item: "Salatalık", amount: "1 adet", grams: 100 },
{ item: "Soğan", amount: "1/2 adet", grams: 40 },
{ item: "Biber", amount: "1 adet", grams: 30 },
{ item: "Humus", amount: "100g", grams: 100 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Tam buğday ekmek", amount: "2 dilim", grams: 60 },
],
calories: 400, protein: 16, carbs: 42, fat: 20,
},
{
name: "Havuç Tarator",
ingredients: [
{ item: "Havuç", amount: "300g", grams: 300 },
{ item: "Yoğurt", amount: "200g", grams: 200 },
{ item: "Sarımsak", amount: "2 diş", grams: 6 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Ceviz", amount: "20g", grams: 20 },
{ item: "Tam buğday ekmek", amount: "2 dilim", grams: 60 },
],
calories: 380, protein: 16, carbs: 44, fat: 16,
},
{
name: "İçli Köfte",
ingredients: [
{ item: "İnce bulgur (dış)", amount: "100g", grams: 100 },
{ item: "Dana kıyma (iç)", amount: "80g", grams: 80 },
{ item: "Soğan", amount: "1/2 adet", grams: 40 },
{ item: "Ceviz", amount: "15g", grams: 15 },
{ item: "Maydanoz", amount: "10g", grams: 10 },
{ item: "Yoğurt", amount: "100g", grams: 100 },
],
calories: 470, protein: 28, carbs: 52, fat: 16,
},
{
name: "Balık Izgara & Roka Salatası",
ingredients: [
{ item: "Levrek veya çipura", amount: "200g", grams: 200 },
{ item: "Roka", amount: "50g", grams: 50 },
{ item: "Limon", amount: "1/2 adet", grams: 30 },
{ item: "Zeytinyağı", amount: "1 yemek kaşığı", grams: 14 },
{ item: "Domates", amount: "1/2 adet", grams: 60 },
{ item: "Tam buğday ekmek", amount: "1 dilim", grams: 30 },
],
calories: 380, protein: 42, carbs: 16, fat: 16,
},
];
const TURKISH_SNACKS = [
{ name: "Ayran & Ceviz", ingredients: [{ item: "Ayran", amount: "1 bardak", grams: 200 }, { item: "Ceviz", amount: "30g", grams: 30 }], calories: 240, protein: 10, carbs: 10, fat: 18 },
{ name: "Meyve & Yoğurt", ingredients: [{ item: "Mevsim meyvesi", amount: "1 porsiyon", grams: 150 }, { item: "Yoğurt", amount: "100g", grams: 100 }], calories: 170, protein: 6, carbs: 28, fat: 4 },
{ name: "Kuru Meyve & Fındık", ingredients: [{ item: "Kuru kayısı", amount: "4 adet", grams: 30 }, { item: "Fındık", amount: "20g", grams: 20 }, { item: "Badem", amount: "10g", grams: 10 }], calories: 200, protein: 5, carbs: 22, fat: 12 },
{ name: "Peynirli Kraker", ingredients: [{ item: "Tam buğday kraker", amount: "4 adet", grams: 30 }, { item: "Beyaz peynir", amount: "30g", grams: 30 }], calories: 170, protein: 8, carbs: 18, fat: 8 },
{ name: "Muzlu Süt", ingredients: [{ item: "Muz", amount: "1 adet", grams: 120 }, { item: "Süt (yarım yağlı)", amount: "200ml", grams: 200 }], calories: 210, protein: 8, carbs: 34, fat: 4 },
{ name: "Havuç & Humus", ingredients: [{ item: "Havuç çubukları", amount: "100g", grams: 100 }, { item: "Humus", amount: "50g", grams: 50 }], calories: 160, protein: 6, carbs: 18, fat: 8 },
{ name: "Yumurta (Haşlanmış)", ingredients: [{ item: "Yumurta", amount: "2 adet", grams: 120 }, { item: "Tuz & karabiber", amount: "az", grams: 1 }], calories: 155, protein: 13, carbs: 1, fat: 11 },
{ name: "Elma & Fıstık Ezmesi", ingredients: [{ item: "Elma", amount: "1 adet", grams: 180 }, { item: "Fıstık ezmesi", amount: "1 yemek kaşığı", grams: 16 }], calories: 190, protein: 5, carbs: 28, fat: 8 },
{ name: "Lor Peyniri & Domates", ingredients: [{ item: "Lor peyniri", amount: "80g", grams: 80 }, { item: "Domates", amount: "1/2 adet", grams: 60 }, { item: "Zeytinyağı", amount: "az", grams: 5 }], calories: 140, protein: 12, carbs: 6, fat: 8 },
{ name: "Türk Kahvesi & Hurma", ingredients: [{ item: "Türk kahvesi", amount: "1 fincan", grams: 60 }, { item: "Hurma", amount: "3 adet", grams: 30 }], calories: 100, protein: 1, carbs: 22, fat: 1 },
];
// ==================== USA / DEFAULT MEALS ====================
const USA_BREAKFASTS = [
{
name: "Oatmeal with Berries",
ingredients: [
{ item: "Rolled oats", amount: "80g", grams: 80 },
{ item: "Milk (low-fat)", amount: "200ml", grams: 200 },
{ item: "Mixed berries", amount: "100g", grams: 100 },
{ item: "Honey", amount: "1 tsp", grams: 7 },
{ item: "Almonds (sliced)", amount: "15g", grams: 15 },
],
calories: 400, protein: 16, carbs: 58, fat: 12,
},
{
name: "Scrambled Eggs & Toast",
ingredients: [
{ item: "Eggs", amount: "3", grams: 180 },
{ item: "Whole wheat toast", amount: "2 slices", grams: 60 },
{ item: "Butter", amount: "10g", grams: 10 },
{ item: "Avocado", amount: "1/4", grams: 50 },
{ item: "Cherry tomatoes", amount: "5", grams: 75 },
],
calories: 450, protein: 26, carbs: 30, fat: 26,
},
{
name: "Greek Yogurt Parfait",
ingredients: [
{ item: "Greek yogurt", amount: "200g", grams: 200 },
{ item: "Granola", amount: "40g", grams: 40 },
{ item: "Banana", amount: "1/2", grams: 60 },
{ item: "Honey", amount: "1 tbsp", grams: 20 },
{ item: "Chia seeds", amount: "10g", grams: 10 },
],
calories: 420, protein: 24, carbs: 54, fat: 12,
},
{
name: "Protein Smoothie Bowl",
ingredients: [
{ item: "Banana (frozen)", amount: "1", grams: 120 },
{ item: "Protein powder", amount: "1 scoop (30g)", grams: 30 },
{ item: "Almond milk", amount: "150ml", grams: 150 },
{ item: "Peanut butter", amount: "1 tbsp", grams: 16 },
{ item: "Granola topping", amount: "20g", grams: 20 },
],
calories: 430, protein: 30, carbs: 48, fat: 14,
},
{
name: "Avocado Toast with Egg",
ingredients: [
{ item: "Whole wheat bread", amount: "2 slices", grams: 60 },
{ item: "Avocado", amount: "1/2", grams: 100 },
{ item: "Egg (poached)", amount: "2", grams: 120 },
{ item: "Red pepper flakes", amount: "pinch", grams: 1 },
{ item: "Lemon juice", amount: "1 tsp", grams: 5 },
],
calories: 440, protein: 20, carbs: 32, fat: 26,
},
];
const USA_LUNCHES = [
{
name: "Grilled Chicken Salad",
ingredients: [
{ item: "Chicken breast (grilled)", amount: "180g", grams: 180 },
{ item: "Mixed greens", amount: "100g", grams: 100 },
{ item: "Cherry tomatoes", amount: "80g", grams: 80 },
{ item: "Cucumber", amount: "80g", grams: 80 },
{ item: "Olive oil dressing", amount: "1 tbsp", grams: 14 },
{ item: "Feta cheese", amount: "30g", grams: 30 },
],
calories: 420, protein: 44, carbs: 12, fat: 22,
},
{
name: "Turkey Wrap",
ingredients: [
{ item: "Whole wheat tortilla", amount: "1 large", grams: 60 },
{ item: "Turkey breast slices", amount: "120g", grams: 120 },
{ item: "Lettuce", amount: "30g", grams: 30 },
{ item: "Tomato", amount: "1/2", grams: 60 },
{ item: "Hummus", amount: "30g", grams: 30 },
{ item: "Swiss cheese", amount: "20g", grams: 20 },
],
calories: 400, protein: 36, carbs: 30, fat: 16,
},
{
name: "Salmon & Sweet Potato",
ingredients: [
{ item: "Salmon fillet", amount: "180g", grams: 180 },
{ item: "Sweet potato (baked)", amount: "200g", grams: 200 },
{ item: "Broccoli (steamed)", amount: "100g", grams: 100 },
{ item: "Olive oil", amount: "1 tsp", grams: 5 },
{ item: "Lemon", amount: "1/2", grams: 30 },
],
calories: 500, protein: 40, carbs: 42, fat: 18,
},
{
name: "Chicken & Brown Rice Bowl",
ingredients: [
{ item: "Chicken breast", amount: "180g", grams: 180 },
{ item: "Brown rice (cooked)", amount: "150g", grams: 150 },
{ item: "Mixed vegetables", amount: "100g", grams: 100 },
{ item: "Soy sauce", amount: "1 tbsp", grams: 15 },
{ item: "Sesame oil", amount: "1 tsp", grams: 5 },
],
calories: 480, protein: 42, carbs: 48, fat: 12,
},
{
name: "Tuna Sandwich",
ingredients: [
{ item: "Whole wheat bread", amount: "2 slices", grams: 60 },
{ item: "Canned tuna", amount: "120g", grams: 120 },
{ item: "Greek yogurt (instead of mayo)", amount: "30g", grams: 30 },
{ item: "Celery", amount: "30g", grams: 30 },
{ item: "Lettuce", amount: "20g", grams: 20 },
{ item: "Apple", amount: "1 small", grams: 120 },
],
calories: 420, protein: 38, carbs: 42, fat: 10,
},
];
const USA_DINNERS = [
{
name: "Grilled Steak & Vegetables",
ingredients: [
{ item: "Lean steak", amount: "180g", grams: 180 },
{ item: "Asparagus", amount: "100g", grams: 100 },
{ item: "Baked potato", amount: "150g", grams: 150 },
{ item: "Olive oil", amount: "1 tbsp", grams: 14 },
{ item: "Garlic", amount: "2 cloves", grams: 6 },
],
calories: 480, protein: 42, carbs: 30, fat: 22,
},
{
name: "Baked Chicken Thighs & Quinoa",
ingredients: [
{ item: "Chicken thighs (skinless)", amount: "200g", grams: 200 },
{ item: "Quinoa (cooked)", amount: "150g", grams: 150 },
{ item: "Roasted vegetables", amount: "150g", grams: 150 },
{ item: "Olive oil", amount: "1 tbsp", grams: 14 },
{ item: "Herbs", amount: "mixed", grams: 5 },
],
calories: 520, protein: 44, carbs: 38, fat: 20,
},
{
name: "Shrimp Stir-Fry",
ingredients: [
{ item: "Shrimp", amount: "200g", grams: 200 },
{ item: "Brown rice (cooked)", amount: "150g", grams: 150 },
{ item: "Bell peppers", amount: "100g", grams: 100 },
{ item: "Broccoli", amount: "80g", grams: 80 },
{ item: "Soy sauce", amount: "1 tbsp", grams: 15 },
{ item: "Sesame oil", amount: "1 tsp", grams: 5 },
],
calories: 440, protein: 38, carbs: 46, fat: 10,
},
{
name: "Turkey Meatballs & Pasta",
ingredients: [
{ item: "Ground turkey", amount: "150g", grams: 150 },
{ item: "Whole wheat pasta (cooked)", amount: "150g", grams: 150 },
{ item: "Marinara sauce", amount: "100g", grams: 100 },
{ item: "Parmesan", amount: "15g", grams: 15 },
{ item: "Side salad", amount: "80g", grams: 80 },
],
calories: 500, protein: 38, carbs: 50, fat: 16,
},
{
name: "Baked Cod & Roasted Vegetables",
ingredients: [
{ item: "Cod fillet", amount: "200g", grams: 200 },
{ item: "Zucchini", amount: "100g", grams: 100 },
{ item: "Cherry tomatoes", amount: "80g", grams: 80 },
{ item: "Olive oil", amount: "1 tbsp", grams: 14 },
{ item: "Brown rice (cooked)", amount: "120g", grams: 120 },
],
calories: 420, protein: 40, carbs: 34, fat: 14,
},
];
const USA_SNACKS = [
{ name: "Protein Bar", ingredients: [{ item: "Protein bar", amount: "1", grams: 60 }], calories: 220, protein: 20, carbs: 24, fat: 8 },
{ name: "Apple & Almond Butter", ingredients: [{ item: "Apple", amount: "1 medium", grams: 180 }, { item: "Almond butter", amount: "1 tbsp", grams: 16 }], calories: 190, protein: 4, carbs: 28, fat: 8 },
{ name: "Trail Mix", ingredients: [{ item: "Mixed nuts", amount: "20g", grams: 20 }, { item: "Dried cranberries", amount: "15g", grams: 15 }, { item: "Dark chocolate chips", amount: "10g", grams: 10 }], calories: 200, protein: 5, carbs: 20, fat: 12 },
{ name: "Cottage Cheese & Berries", ingredients: [{ item: "Cottage cheese", amount: "150g", grams: 150 }, { item: "Mixed berries", amount: "80g", grams: 80 }], calories: 170, protein: 18, carbs: 16, fat: 4 },
{ name: "Veggie Sticks & Hummus", ingredients: [{ item: "Carrot sticks", amount: "60g", grams: 60 }, { item: "Celery sticks", amount: "40g", grams: 40 }, { item: "Hummus", amount: "50g", grams: 50 }], calories: 150, protein: 6, carbs: 16, fat: 8 },
{ name: "Hard Boiled Eggs", ingredients: [{ item: "Eggs", amount: "2", grams: 120 }], calories: 155, protein: 13, carbs: 1, fat: 11 },
{ name: "Greek Yogurt & Honey", ingredients: [{ item: "Greek yogurt", amount: "150g", grams: 150 }, { item: "Honey", amount: "1 tsp", grams: 7 }], calories: 160, protein: 15, carbs: 16, fat: 4 },
{ name: "Banana & Peanut Butter", ingredients: [{ item: "Banana", amount: "1", grams: 120 }, { item: "Peanut butter", amount: "1 tbsp", grams: 16 }], calories: 210, protein: 6, carbs: 30, fat: 9 },
];
function getMealsByCountry(country) {
const c = (country || "").toLowerCase().trim();
if (c === "turkey" || c === "türkiye" || c === "turkiye") {
return {
breakfasts: TURKISH_BREAKFASTS,
lunches: TURKISH_LUNCHES,
dinners: TURKISH_DINNERS,
snacks: TURKISH_SNACKS,
};
}
if (c === "usa" || c === "united states" || c === "us" || c === "america") {
return {
breakfasts: USA_BREAKFASTS,
lunches: USA_LUNCHES,
dinners: USA_DINNERS,
snacks: USA_SNACKS,
};
}
// Default: Mediterranean mix (use USA meals as base)
return {
breakfasts: USA_BREAKFASTS,
lunches: USA_LUNCHES,
dinners: USA_DINNERS,
snacks: USA_SNACKS,
};
}
function scaleMeal(meal, targetCalories, mealCalorieShare) {
const target = targetCalories * mealCalorieShare;
const ratio = target / meal.calories;
return {
...meal,
calories: Math.round(meal.calories * ratio),
protein: Math.round(meal.protein * ratio),
carbs: Math.round(meal.carbs * ratio),
fat: Math.round(meal.fat * ratio),
ingredients: meal.ingredients.map((ing) => ({
...ing,
grams: Math.round(ing.grams * ratio),
})),
};
}
function pickDifferent(arr, count, usedIndices) {
const available = arr.map((item, idx) => ({ item, idx })).filter((x) => !usedIndices.has(x.idx));
const shuffled = available.sort(() => 0.5 - Math.random());
const picked = shuffled.slice(0, count);
picked.forEach((p) => usedIndices.add(p.idx));
if (picked.length < count) {
// Reset and pick more if needed
const extra = arr.map((item, idx) => ({ item, idx })).sort(() => 0.5 - Math.random()).slice(0, count - picked.length);
return [...picked.map((p) => p.item), ...extra.map((p) => p.item)];
}
return picked.map((p) => p.item);
}
function generateMealPlan(profile) {
const { weight, height, age, gender, activity_level, goal, country } = profile;
const bmr = calculateBMR(weight, height, age, gender);
const tdee = calculateTDEE(bmr, activity_level);
const targetCalories = Math.round(adjustCalories(tdee, goal));
const macros = calculateMacros(targetCalories, goal);
const meals = getMealsByCountry(country);
// Meal calorie distribution: breakfast 25%, snack1 10%, lunch 30%, snack2 10%, dinner 25%
const shares = { breakfast: 0.25, snack1: 0.10, lunch: 0.30, snack2: 0.10, dinner: 0.25 };
const dayNames = ["Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi", "Pazar"];
const usedBreakfasts = new Set();
const usedLunches = new Set();
const usedDinners = new Set();
const usedSnacks1 = new Set();
const usedSnacks2 = new Set();
const days = dayNames.map((dayName) => {
const breakfast = scaleMeal(pickDifferent(meals.breakfasts, 1, usedBreakfasts)[0], targetCalories, shares.breakfast);
const snack1 = scaleMeal(pickDifferent(meals.snacks, 1, usedSnacks1)[0], targetCalories, shares.snack1);
const lunch = scaleMeal(pickDifferent(meals.lunches, 1, usedLunches)[0], targetCalories, shares.lunch);
const snack2 = scaleMeal(pickDifferent(meals.snacks, 1, usedSnacks2)[0], targetCalories, shares.snack2);
const dinner = scaleMeal(pickDifferent(meals.dinners, 1, usedDinners)[0], targetCalories, shares.dinner);
const dayCalories = breakfast.calories + snack1.calories + lunch.calories + snack2.calories + dinner.calories;
return {
day: dayName,
total_calories: dayCalories,
meals: [
{ type: "Kahvaltı", ...breakfast },
{ type: "Ara Öğün 1", ...snack1 },
{ type: "Öğle Yemeği", ...lunch },
{ type: "Ara Öğün 2", ...snack2 },
{ type: "Akşam Yemeği", ...dinner },
],
};
});
return {
title: "Kişisel Beslenme Programı",
bmr: Math.round(bmr),
tdee: Math.round(tdee),
target_calories: targetCalories,
macros,
goal,
country: country || "Turkey",
notes: [
`Günlük hedef kalori: ${targetCalories} kcal`,
`Protein: ${macros.protein_g}g | Karbonhidrat: ${macros.carbs_g}g | Yağ: ${macros.fat_g}g`,
"Günde en az 2.5-3 litre su için",
"Öğün saatlerinizi düzenli tutun",
"Yemekleri yavaş yiyin, iyi çiğneyin",
],
days,
};
}
module.exports = { generateMealPlan };

162
src/services/shopping.js Normal file
View File

@@ -0,0 +1,162 @@
"use strict";
const CATEGORY_MAP = {
// Protein
"yumurta": "protein", "eggs": "protein", "egg": "protein",
"tavuk": "protein", "chicken": "protein", "turkey breast": "protein",
"dana": "protein", "beef": "protein", "steak": "protein", "lean steak": "protein",
"kıyma": "protein", "ground turkey": "protein", "ground beef": "protein",
"sucuk": "protein", "köfte": "protein", "kebap": "protein", "adana": "protein",
"balık": "protein", "levrek": "protein", "çipura": "protein", "salmon": "protein",
"cod": "protein", "tuna": "protein", "shrimp": "protein",
"et ": "protein", "kuşbaşı": "protein", "mantı": "protein",
"lahmacun": "protein", "pide hamuru": "protein",
"protein powder": "protein", "protein bar": "protein",
// Dairy
"peynir": "dairy", "cheese": "dairy", "feta": "dairy", "parmesan": "dairy", "swiss": "dairy",
"süt": "dairy", "milk": "dairy", "almond milk": "dairy",
"yoğurt": "dairy", "yogurt": "dairy", "greek yogurt": "dairy", "cottage cheese": "dairy",
"kaymak": "dairy", "tereyağı": "dairy", "butter": "dairy",
"ayran": "dairy", "lor": "dairy", "kaşar": "dairy",
"cream": "dairy",
// Vegetables
"domates": "vegetables", "tomato": "vegetables", "cherry tomato": "vegetables",
"salatalık": "vegetables", "cucumber": "vegetables",
"biber": "vegetables", "bell pepper": "vegetables", "pepper": "vegetables",
"soğan": "vegetables", "onion": "vegetables",
"sarımsak": "vegetables", "garlic": "vegetables",
"patlıcan": "vegetables", "havuç": "vegetables", "carrot": "vegetables",
"kabak": "vegetables", "zucchini": "vegetables",
"ıspanak": "vegetables", "ispanak": "vegetables", "spinach": "vegetables",
"marul": "vegetables", "lettuce": "vegetables", "mixed greens": "vegetables",
"roka": "vegetables", "asparagus": "vegetables", "broccoli": "vegetables",
"fasulye": "vegetables", "bamya": "vegetables", "taze fasulye": "vegetables",
"maydanoz": "vegetables", "dereotu": "vegetables", "nane": "vegetables",
"asma yaprağı": "vegetables", "celery": "vegetables",
"roasted vegetables": "vegetables", "mixed vegetables": "vegetables",
"sweet potato": "vegetables", "baked potato": "vegetables", "potato": "vegetables",
// Fruits
"muz": "fruits", "banana": "fruits",
"elma": "fruits", "apple": "fruits",
"meyve": "fruits", "berries": "fruits", "mixed berries": "fruits",
"limon": "fruits", "lemon": "fruits",
"nar ekşisi": "fruits", "cranberries": "fruits",
"hurma": "fruits", "kuru kayısı": "fruits",
// Grains
"bulgur": "grains", "pirinç": "grains", "pilav": "grains",
"rice": "grains", "brown rice": "grains", "quinoa": "grains",
"ekmek": "grains", "bread": "grains", "whole wheat": "grains", "toast": "grains",
"simit": "grains", "lavaş": "grains", "tortilla": "grains",
"un": "grains", "yufka": "grains",
"mercimek": "grains", "kuru fasulye": "grains", "nohut": "grains",
"oats": "grains", "granola": "grains", "pasta": "grains",
"chia": "grains", "kraker": "grains",
// Spices & Condiments
"pul biber": "spices", "tuz": "spices", "karabiber": "spices",
"sumak": "spices", "herbs": "spices", "red pepper": "spices",
"salça": "spices", "domates salçası": "spices",
"soy sauce": "spices", "sesame oil": "spices", "marinara": "spices",
"honey": "spices", "bal": "spices",
// Fats & Nuts
"zeytinyağı": "fats_nuts", "olive oil": "fats_nuts",
"zeytin": "fats_nuts",
"ceviz": "fats_nuts", "fındık": "fats_nuts", "badem": "fats_nuts",
"almonds": "fats_nuts", "peanut butter": "fats_nuts", "almond butter": "fats_nuts",
"fıstık ezmesi": "fats_nuts", "avocado": "fats_nuts",
"mixed nuts": "fats_nuts", "dark chocolate": "fats_nuts",
"humus": "fats_nuts",
// Beverages
"çay": "beverages", "kahve": "beverages", "türk kahvesi": "beverages",
"şalgam": "beverages",
"su": "beverages", "water": "beverages",
// Other
"turşu": "other",
};
function categorizeIngredient(itemName) {
const lower = itemName.toLowerCase();
for (const [keyword, category] of Object.entries(CATEGORY_MAP)) {
if (lower.includes(keyword)) {
return category;
}
}
return "other";
}
function generateShoppingList(mealPlan) {
const ingredientMap = {};
for (const day of mealPlan.days) {
for (const meal of day.meals) {
for (const ing of meal.ingredients) {
const key = ing.item.toLowerCase().trim();
if (!ingredientMap[key]) {
ingredientMap[key] = {
item: ing.item,
total_grams: 0,
category: categorizeIngredient(ing.item),
};
}
ingredientMap[key].total_grams += ing.grams;
}
}
}
const categories = {
protein: { name: "Protein Kaynakları", icon: "🥩", items: [] },
dairy: { name: "Süt Ürünleri", icon: "🧀", items: [] },
vegetables: { name: "Sebzeler", icon: "🥬", items: [] },
fruits: { name: "Meyveler", icon: "🍎", items: [] },
grains: { name: "Tahıllar & Baklagiller", icon: "🌾", items: [] },
fats_nuts: { name: "Yağlar & Kuruyemişler", icon: "🥜", items: [] },
spices: { name: "Baharat & Sos", icon: "🧂", items: [] },
beverages: { name: "İçecekler", icon: "🥤", items: [] },
other: { name: "Diğer", icon: "📦", items: [] },
};
for (const data of Object.values(ingredientMap)) {
const cat = categories[data.category] || categories.other;
cat.items.push({
item: data.item,
total_grams: Math.round(data.total_grams),
display_amount: formatAmount(data.total_grams),
});
}
// Sort items within each category alphabetically
for (const cat of Object.values(categories)) {
cat.items.sort((a, b) => a.item.localeCompare(b.item, "tr"));
}
// Remove empty categories
const result = {};
for (const [key, cat] of Object.entries(categories)) {
if (cat.items.length > 0) {
result[key] = cat;
}
}
return {
title: "Haftalık Alışveriş Listesi",
note: "Bu liste 7 günlük beslenme programınıza göre hazırlanmıştır.",
categories: result,
total_items: Object.values(ingredientMap).length,
};
}
function formatAmount(grams) {
if (grams >= 1000) {
return `${(grams / 1000).toFixed(1)} kg`;
}
return `${grams}g`;
}
module.exports = { generateShoppingList };

371
src/services/trainer.js Normal file
View File

@@ -0,0 +1,371 @@
"use strict";
const EXERCISES = {
chest: [
{ name: "Bench Press (Göğüs Presi)", sets: 4, reps: "8-10", rest_seconds: 90, notes: "Kontrollü iniş, göğüs kaslarını sıkın" },
{ name: "Incline Dumbbell Press (Üst Göğüs)", sets: 4, reps: "10-12", rest_seconds: 75, notes: "30 derece açı, tam hareket açıklığı" },
{ name: "Cable Flyes (Kablo Çapraz)", sets: 3, reps: "12-15", rest_seconds: 60, notes: "Kollar hafif bükük, göğüs merkezinde sıkın" },
{ name: "Dips (Paralel Bar)", sets: 3, reps: "10-12", rest_seconds: 75, notes: "Öne eğilin, göğüs hedefleyin" },
{ name: "Push-ups (Şınav)", sets: 3, reps: "15-20", rest_seconds: 45, notes: "Geniş tutuş, göğüs yere değsin" },
{ name: "Dumbbell Pullover", sets: 3, reps: "12", rest_seconds: 60, notes: "Kollar hafif bükük, göğüs gerilmesini hissedin" },
],
back: [
{ name: "Deadlift (Toplu Kaldırma)", sets: 4, reps: "6-8", rest_seconds: 120, notes: "Sırt düz, kalçadan kaldırın" },
{ name: "Barbell Row (Sırt Çekişi)", sets: 4, reps: "8-10", rest_seconds: 90, notes: "Gövde yere paralel, dirsekler geri" },
{ name: "Lat Pulldown (Lat Çekme)", sets: 4, reps: "10-12", rest_seconds: 75, notes: "Geniş tutuş, göğüse çekin" },
{ name: "Seated Cable Row (Oturarak Çekiş)", sets: 3, reps: "10-12", rest_seconds: 75, notes: "Sırt düz, kürek kemiklerini sıkın" },
{ name: "T-Bar Row", sets: 3, reps: "10-12", rest_seconds: 75, notes: "Göğüse doğru çekin, sırt kaslarını sıkın" },
{ name: "Face Pull (Yüz Çekişi)", sets: 3, reps: "15", rest_seconds: 45, notes: "Omuz arkası ve üst sırt hedefli" },
],
legs: [
{ name: "Squat (Çömelme)", sets: 4, reps: "8-10", rest_seconds: 120, notes: "Diz açısı 90 derece, sırt düz" },
{ name: "Romanian Deadlift (Romen Kaldırma)", sets: 4, reps: "10-12", rest_seconds: 90, notes: "Bacak arkası gerilmeli, kalça menteşesi" },
{ name: "Leg Press (Bacak Presi)", sets: 4, reps: "10-12", rest_seconds: 90, notes: "Ayaklar omuz genişliğinde" },
{ name: "Walking Lunges (Yürüyüş Hamle)", sets: 3, reps: "12 (her bacak)", rest_seconds: 75, notes: "Diz parmak ucunu geçmesin" },
{ name: "Leg Curl (Bacak Büküm)", sets: 3, reps: "12-15", rest_seconds: 60, notes: "Kontrollü hareket, hamstring sıkın" },
{ name: "Leg Extension (Bacak Açma)", sets: 3, reps: "12-15", rest_seconds: 60, notes: "Tepe noktada sıkın" },
{ name: "Calf Raise (Baldır Kaldırma)", sets: 4, reps: "15-20", rest_seconds: 45, notes: "Tam gerilme ve tam kasılma" },
],
shoulders: [
{ name: "Overhead Press (Omuz Presi)", sets: 4, reps: "8-10", rest_seconds: 90, notes: "Ayakta, core sıkı, başın üstüne itin" },
{ name: "Lateral Raise (Yan Kaldırma)", sets: 4, reps: "12-15", rest_seconds: 60, notes: "Hafif ağırlık, kontrollü hareket" },
{ name: "Front Raise (Ön Kaldırma)", sets: 3, reps: "12", rest_seconds: 60, notes: "Dönüşümlü kollarla" },
{ name: "Reverse Fly (Ters Sinek)", sets: 3, reps: "15", rest_seconds: 60, notes: "Arka omuz hedefli, kürek kemiklerini sıkın" },
{ name: "Arnold Press", sets: 3, reps: "10-12", rest_seconds: 75, notes: "Rotasyonlu hareket, omuzun 3 başını çalıştırır" },
{ name: "Shrugs (Omuz Silkme)", sets: 3, reps: "12-15", rest_seconds: 60, notes: "Trapez hedefli, yukarı çekin" },
],
arms: [
{ name: "Barbell Curl (Halter Biceps)", sets: 4, reps: "10-12", rest_seconds: 60, notes: "Dirsekler sabit, biceps sıkın" },
{ name: "Triceps Pushdown (Triceps İtme)", sets: 4, reps: "10-12", rest_seconds: 60, notes: "Dirsekler vücuda yapışık" },
{ name: "Hammer Curl (Çekiç Curl)", sets: 3, reps: "12", rest_seconds: 60, notes: "Nötr tutuş, ön kol da çalışır" },
{ name: "Overhead Triceps Extension", sets: 3, reps: "12", rest_seconds: 60, notes: "Dirsekler sabit, tam gerilme" },
{ name: "Concentration Curl (Konsantrasyon)", sets: 3, reps: "12 (her kol)", rest_seconds: 45, notes: "Yavaş ve kontrollü" },
{ name: "Skull Crushers", sets: 3, reps: "10-12", rest_seconds: 60, notes: "Alına doğru indirin, triceps sıkın" },
{ name: "Wrist Curl (Bilek Büküm)", sets: 2, reps: "15-20", rest_seconds: 30, notes: "Ön kol güçlendirme" },
],
cardio: [
{ name: "Koşu Bandı (Tempolu Koşu)", sets: 1, reps: "25 dakika", rest_seconds: 0, notes: "Kalp atış hızı %65-75 arası" },
{ name: "Bisiklet", sets: 1, reps: "20 dakika", rest_seconds: 0, notes: "Orta direnç, stabil tempo" },
{ name: "Kürek Çekme Makinesi", sets: 1, reps: "15 dakika", rest_seconds: 0, notes: "Tüm vücut kardiyosu" },
{ name: "Merdiven Çıkma (StairMaster)", sets: 1, reps: "15 dakika", rest_seconds: 0, notes: "Bacak ve kalça hedefli" },
{ name: "Eliptik Bisiklet", sets: 1, reps: "20 dakika", rest_seconds: 0, notes: "Düşük etki, tüm vücut" },
],
hiit: [
{ name: "Burpee", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Maksimum efor" },
{ name: "Mountain Climber (Dağcı)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Core sıkı, hızlı hareket" },
{ name: "Jump Squat (Sıçramalı Çömelme)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Yumuşak iniş, diz koruması" },
{ name: "High Knees (Yüksek Diz)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Dizleri göğüse çekin" },
{ name: "Box Jump (Kutu Atlama)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "İki ayakla atlayıp yumuşak inin" },
{ name: "Battle Ropes (Savaş İpleri)", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Kollar ve core hedefli" },
{ name: "Kettlebell Swing", sets: 4, reps: "30 saniye", rest_seconds: 30, notes: "Kalça menteşesi, explosive hareket" },
],
core: [
{ name: "Plank (Düz Plank)", sets: 3, reps: "45 saniye", rest_seconds: 30, notes: "Vücut düz çizgi, core sıkı" },
{ name: "Russian Twist (Rus Bükülmesi)", sets: 3, reps: "20 (her taraf)", rest_seconds: 30, notes: "Ayaklar yerden, kontrollü rotasyon" },
{ name: "Bicycle Crunch (Bisiklet Karın)", sets: 3, reps: "20 (her taraf)", rest_seconds: 30, notes: "Karşı diz-dirsek buluşsun" },
{ name: "Leg Raise (Bacak Kaldırma)", sets: 3, reps: "15", rest_seconds: 30, notes: "Alt karın hedefli, sırt yere yapışık" },
{ name: "Dead Bug", sets: 3, reps: "12 (her taraf)", rest_seconds: 30, notes: "Sırt yere yapışık, kontrollü hareket" },
{ name: "Side Plank (Yan Plank)", sets: 2, reps: "30 saniye (her taraf)", rest_seconds: 30, notes: "Kalça düşmesin" },
],
flexibility: [
{ name: "Kedi-İnek Germe", sets: 1, reps: "10 tekrar", rest_seconds: 0, notes: "Nefesle senkronize" },
{ name: "Hamstring Germe", sets: 1, reps: "30 saniye (her bacak)", rest_seconds: 0, notes: "Bacak arkasını hissedin" },
{ name: "Quadriceps Germe", sets: 1, reps: "30 saniye (her bacak)", rest_seconds: 0, notes: "Dengeyi koruyun" },
{ name: "Güvercin Pozu", sets: 1, reps: "30 saniye (her taraf)", rest_seconds: 0, notes: "Kalça açıcı" },
{ name: "Çocuk Pozu", sets: 1, reps: "45 saniye", rest_seconds: 0, notes: "Sırt ve kalça rahatlama" },
{ name: "Kelebek Germe", sets: 1, reps: "30 saniye", rest_seconds: 0, notes: "İç bacak ve kasık" },
{ name: "Omuz Çapraz Germe", sets: 1, reps: "30 saniye (her kol)", rest_seconds: 0, notes: "Omuz esnekliği" },
{ name: "Aşağı Bakan Köpek", sets: 1, reps: "45 saniye", rest_seconds: 0, notes: "Tüm arka zincir" },
{ name: "Dünya Germe (World's Greatest Stretch)", sets: 1, reps: "5 tekrar (her taraf)", rest_seconds: 0, notes: "Tüm vücut mobilitesi" },
{ name: "Foam Rolling - Sırt", sets: 1, reps: "60 saniye", rest_seconds: 0, notes: "Yavaş ve kontrollü" },
{ name: "Foam Rolling - Bacaklar", sets: 1, reps: "60 saniye (her bacak)", rest_seconds: 0, notes: "Ağrılı noktada durun" },
],
compound: [
{ name: "Squat (Çömelme)", sets: 5, reps: "5", rest_seconds: 180, notes: "Ağır, compound hareket, güç odaklı" },
{ name: "Bench Press (Göğüs Presi)", sets: 5, reps: "5", rest_seconds: 180, notes: "Ağır yükleme, temel hareket" },
{ name: "Deadlift (Toplu Kaldırma)", sets: 5, reps: "5", rest_seconds: 180, notes: "En güçlü compound hareket" },
{ name: "Overhead Press (Omuz Presi)", sets: 5, reps: "5", rest_seconds: 150, notes: "Ayakta, tüm üst vücut" },
{ name: "Barbell Row (Sırt Çekişi)", sets: 5, reps: "5", rest_seconds: 150, notes: "Ağır, sırt kalınlığı" },
],
circuit: [
{ name: "Kettlebell Swing", sets: 3, reps: "15", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Push-ups (Şınav)", sets: 3, reps: "15", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Goblet Squat", sets: 3, reps: "15", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Dumbbell Row", sets: 3, reps: "12 (her kol)", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Jumping Lunges", sets: 3, reps: "12 (her bacak)", rest_seconds: 15, notes: "Hızlı geçiş" },
{ name: "Plank", sets: 3, reps: "30 saniye", rest_seconds: 60, notes: "Devrenin sonu, 60sn dinlenme sonra tekrar" },
],
};
function pickRandom(arr, count) {
const shuffled = [...arr].sort(() => 0.5 - Math.random());
return shuffled.slice(0, count);
}
function adjustForAge(exercises, age) {
if (age > 55) {
return exercises.map((e) => ({
...e,
sets: Math.max(2, e.sets - 1),
notes: e.notes + " | Yaşa uygun: Ağırlığı azaltın, kontrollü hareket",
}));
}
if (age < 20) {
return exercises.map((e) => ({
...e,
notes: e.notes + " | Genç sporcu: Form ve tekniğe odaklanın",
}));
}
return exercises;
}
function generateLoseWeightProgram(profile) {
const { age, gender } = profile;
const days = [
{
day: "Pazartesi",
type: "cardio",
title: "HIIT Kardiyo",
exercises: adjustForAge([...pickRandom(EXERCISES.hiit, 5), ...pickRandom(EXERCISES.core, 3)], age),
},
{
day: "Salı",
type: "strength",
title: "Üst Vücut Devre",
exercises: adjustForAge([...pickRandom(EXERCISES.chest, 2), ...pickRandom(EXERCISES.back, 2), ...pickRandom(EXERCISES.shoulders, 2), ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Çarşamba",
type: "cardio",
title: "Kardiyo & Core",
exercises: adjustForAge([...pickRandom(EXERCISES.cardio, 3), ...pickRandom(EXERCISES.core, 4)], age),
},
{
day: "Perşembe",
type: "strength",
title: "Alt Vücut Devre",
exercises: adjustForAge([...pickRandom(EXERCISES.legs, 4), ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Cuma",
type: "cardio",
title: "HIIT & Circuit",
exercises: adjustForAge(EXERCISES.circuit, age),
},
{
day: "Cumartesi",
type: "cardio",
title: "Uzun Kardiyo",
exercises: adjustForAge([
{ name: "Açık Hava Koşusu veya Yürüyüş", sets: 1, reps: "45-60 dakika", rest_seconds: 0, notes: "Orta tempo, yağ yakım bölgesi" },
...pickRandom(EXERCISES.flexibility, 4),
], age),
},
{
day: "Pazar",
type: "rest",
title: "Dinlenme & İyileşme",
exercises: adjustForAge(EXERCISES.flexibility, age),
},
];
return days;
}
function generateBuildMuscleProgram(profile) {
const { age, gender } = profile;
const days = [
{
day: "Pazartesi",
type: "strength",
title: "Göğüs & Triceps",
exercises: adjustForAge([...EXERCISES.chest.slice(0, 4), ...EXERCISES.arms.filter((e) => e.name.includes("Triceps") || e.name.includes("Skull")).slice(0, 3)], age),
},
{
day: "Salı",
type: "strength",
title: "Sırt & Biceps",
exercises: adjustForAge([...EXERCISES.back.slice(0, 4), ...EXERCISES.arms.filter((e) => e.name.includes("Curl") || e.name.includes("Hammer")).slice(0, 3)], age),
},
{
day: "Çarşamba",
type: "strength",
title: "Bacak Günü",
exercises: adjustForAge(EXERCISES.legs, age),
},
{
day: "Perşembe",
type: "rest",
title: "Aktif Dinlenme",
exercises: adjustForAge([
{ name: "Hafif Yürüyüş", sets: 1, reps: "20 dakika", rest_seconds: 0, notes: "Kan dolaşımı için" },
...pickRandom(EXERCISES.flexibility, 5),
], age),
},
{
day: "Cuma",
type: "strength",
title: "Omuz & Trapez",
exercises: adjustForAge([...EXERCISES.shoulders, ...pickRandom(EXERCISES.core, 3)], age),
},
{
day: "Cumartesi",
type: "strength",
title: "Kol Günü (Biceps & Triceps)",
exercises: adjustForAge([...EXERCISES.arms, ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Pazar",
type: "rest",
title: "Tam Dinlenme",
exercises: adjustForAge(EXERCISES.flexibility, age),
},
];
return days;
}
function generateGainWeightProgram(profile) {
const { age, gender } = profile;
const days = [
{
day: "Pazartesi",
type: "strength",
title: "Compound Güç - Üst Vücut",
exercises: adjustForAge([...EXERCISES.compound.filter((e) => e.name.includes("Bench") || e.name.includes("Overhead") || e.name.includes("Row")), ...pickRandom(EXERCISES.chest, 2), ...pickRandom(EXERCISES.shoulders, 1)], age),
},
{
day: "Salı",
type: "strength",
title: "Compound Güç - Alt Vücut",
exercises: adjustForAge([...EXERCISES.compound.filter((e) => e.name.includes("Squat") || e.name.includes("Deadlift")), ...pickRandom(EXERCISES.legs, 3)], age),
},
{
day: "Çarşamba",
type: "rest",
title: "Dinlenme & Toparlanma",
exercises: adjustForAge(pickRandom(EXERCISES.flexibility, 6), age),
},
{
day: "Perşembe",
type: "strength",
title: "Göğüs, Sırt & Omuz",
exercises: adjustForAge([...pickRandom(EXERCISES.chest, 3), ...pickRandom(EXERCISES.back, 3), ...pickRandom(EXERCISES.shoulders, 2)], age),
},
{
day: "Cuma",
type: "strength",
title: "Bacak & Core",
exercises: adjustForAge([...pickRandom(EXERCISES.legs, 5), ...pickRandom(EXERCISES.core, 3)], age),
},
{
day: "Cumartesi",
type: "strength",
title: "Kollar & Yardımcı Kaslar",
exercises: adjustForAge([...EXERCISES.arms, ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Pazar",
type: "rest",
title: "Tam Dinlenme",
exercises: adjustForAge(EXERCISES.flexibility, age),
},
];
return days;
}
function generateMaintainProgram(profile) {
const { age, gender } = profile;
const days = [
{
day: "Pazartesi",
type: "strength",
title: "Üst Vücut Kuvvet",
exercises: adjustForAge([...pickRandom(EXERCISES.chest, 2), ...pickRandom(EXERCISES.back, 2), ...pickRandom(EXERCISES.shoulders, 2)], age),
},
{
day: "Salı",
type: "cardio",
title: "Kardiyo & Core",
exercises: adjustForAge([...pickRandom(EXERCISES.cardio, 2), ...pickRandom(EXERCISES.core, 4)], age),
},
{
day: "Çarşamba",
type: "strength",
title: "Alt Vücut Kuvvet",
exercises: adjustForAge(pickRandom(EXERCISES.legs, 5), age),
},
{
day: "Perşembe",
type: "flexibility",
title: "Esneklik & Mobilite",
exercises: adjustForAge(EXERCISES.flexibility, age),
},
{
day: "Cuma",
type: "strength",
title: "Tüm Vücut",
exercises: adjustForAge([...pickRandom(EXERCISES.compound, 3), ...pickRandom(EXERCISES.arms, 2), ...pickRandom(EXERCISES.core, 2)], age),
},
{
day: "Cumartesi",
type: "cardio",
title: "Açık Hava Aktivitesi",
exercises: adjustForAge([
{ name: "Yürüyüş veya Koşu", sets: 1, reps: "30-45 dakika", rest_seconds: 0, notes: "Doğada, orta tempo" },
{ name: "Bisiklet veya Yüzme", sets: 1, reps: "30 dakika", rest_seconds: 0, notes: "Keyifli tempo" },
...pickRandom(EXERCISES.flexibility, 3),
], age),
},
{
day: "Pazar",
type: "rest",
title: "Tam Dinlenme",
exercises: adjustForAge(pickRandom(EXERCISES.flexibility, 5), age),
},
];
return days;
}
function generateWorkoutProgram(profile) {
const goalMap = {
lose_weight: generateLoseWeightProgram,
build_muscle: generateBuildMuscleProgram,
gain_weight: generateGainWeightProgram,
maintain: generateMaintainProgram,
};
const generator = goalMap[profile.goal] || goalMap.maintain;
const program = generator(profile);
const goalNames = {
lose_weight: "Yağ Yakma & Zayıflama",
build_muscle: "Kas Geliştirme",
gain_weight: "Kilo Alma & Güçlenme",
maintain: "Formu Koruma",
};
const activityNotes = {
sedentary: "Başlangıç seviyesi - yavaş başlayın, formu öğrenin",
light: "Hafif seviye - ağırlıkları kademeli artırın",
moderate: "Orta seviye - standart program",
active: "İleri seviye - ağırlıkları artırabilirsiniz",
very_active: "Elit seviye - süper setler ve drop setler ekleyebilirsiniz",
};
return {
title: `${goalNames[profile.goal] || "Fitness"} Programı`,
goal: profile.goal,
level: profile.activity_level,
note: activityNotes[profile.activity_level] || "",
general_tips: [
"Her antrenmandan önce 5-10 dakika ısınma yapın",
"Her antrenmandan sonra 5-10 dakika soğuma ve germe yapın",
"Günde en az 2-3 litre su için",
"Uyku düzeninize dikkat edin (7-9 saat)",
"Ağrı hissederseniz hareketi durdurun",
"Haftada en az 1 gün tam dinlenme yapın",
],
days: program,
};
}
module.exports = { generateWorkoutProgram };