mirror of
https://github.com/sstent/foodplanner.git
synced 2026-03-13 08:45:24 +00:00
fixed add meal item
This commit is contained in:
114
main.py
114
main.py
@@ -46,6 +46,7 @@ class Food(Base):
|
||||
sodium = Column(Float, default=0)
|
||||
calcium = Column(Float, default=0)
|
||||
source = Column(String, default="manual") # manual, csv, openfoodfacts
|
||||
brand = Column(String, default="") # Brand name for the food
|
||||
|
||||
class Meal(Base):
|
||||
__tablename__ = "meals"
|
||||
@@ -53,6 +54,7 @@ class Meal(Base):
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String, index=True)
|
||||
meal_type = Column(String) # breakfast, lunch, dinner, snack, custom
|
||||
meal_time = Column(String, default="Breakfast") # Breakfast, Lunch, Dinner, Snack 1, Snack 2, Beverage 1, Beverage 2
|
||||
|
||||
# Relationship to meal foods
|
||||
meal_foods = relationship("MealFood", back_populates="meal")
|
||||
@@ -75,7 +77,7 @@ class Plan(Base):
|
||||
person = Column(String, index=True) # Person A or Person B
|
||||
date = Column(Date, index=True) # Store actual calendar dates
|
||||
meal_id = Column(Integer, ForeignKey("meals.id"))
|
||||
meal_time = Column(String, default="Breakfast") # Breakfast, Lunch, Dinner, Snack 1, Snack 2, Beverage 1, Beverage 2
|
||||
meal_time = Column(String) # Breakfast, Lunch, Dinner, Snack 1, Snack 2, Beverage 1, Beverage 2
|
||||
|
||||
meal = relationship("Meal")
|
||||
|
||||
@@ -113,6 +115,7 @@ class FoodCreate(BaseModel):
|
||||
sodium: float = 0
|
||||
calcium: float = 0
|
||||
source: str = "manual"
|
||||
brand: Optional[str] = ""
|
||||
|
||||
class FoodResponse(BaseModel):
|
||||
id: int
|
||||
@@ -128,6 +131,7 @@ class FoodResponse(BaseModel):
|
||||
sodium: float
|
||||
calcium: float
|
||||
source: str
|
||||
brand: str
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
@@ -135,6 +139,7 @@ class FoodResponse(BaseModel):
|
||||
class MealCreate(BaseModel):
|
||||
name: str
|
||||
meal_type: str
|
||||
meal_time: str
|
||||
foods: List[dict] # [{"food_id": 1, "quantity": 1.5}]
|
||||
|
||||
# Database dependency
|
||||
@@ -252,7 +257,8 @@ async def bulk_upload_foods(file: UploadFile = File(...), db: Session = Depends(
|
||||
'fiber': round(float(row.get('Fiber (g)', 0)), 2),
|
||||
'sugar': round(float(row.get('Sugar (g)', 0)), 2),
|
||||
'sodium': round(float(row.get('Sodium (mg)', 0)), 2),
|
||||
'calcium': round(float(row.get('Calcium (mg)', 0)), 2)
|
||||
'calcium': round(float(row.get('Calcium (mg)', 0)), 2),
|
||||
'brand': row.get('Brand', '') # Add brand from CSV
|
||||
}
|
||||
|
||||
# Check for existing food
|
||||
@@ -290,14 +296,15 @@ async def add_food(request: Request, db: Session = Depends(get_db),
|
||||
protein: float = Form(...), carbs: float = Form(...),
|
||||
fat: float = Form(...), fiber: float = Form(0),
|
||||
sugar: float = Form(0), sodium: float = Form(0),
|
||||
calcium: float = Form(0), source: str = Form("manual")):
|
||||
calcium: float = Form(0), source: str = Form("manual"),
|
||||
brand: str = Form("")):
|
||||
|
||||
try:
|
||||
food = Food(
|
||||
name=name, serving_size=serving_size, serving_unit=serving_unit,
|
||||
calories=calories, protein=protein, carbs=carbs, fat=fat,
|
||||
fiber=fiber, sugar=sugar, sodium=sodium, calcium=calcium,
|
||||
source=source
|
||||
source=source, brand=brand
|
||||
)
|
||||
db.add(food)
|
||||
db.commit()
|
||||
@@ -314,7 +321,7 @@ async def edit_food(request: Request, db: Session = Depends(get_db),
|
||||
carbs: float = Form(...), fat: float = Form(...),
|
||||
fiber: float = Form(0), sugar: float = Form(0),
|
||||
sodium: float = Form(0), calcium: float = Form(0),
|
||||
source: str = Form("manual")):
|
||||
source: str = Form("manual"), brand: str = Form("")):
|
||||
|
||||
try:
|
||||
food = db.query(Food).filter(Food.id == food_id).first()
|
||||
@@ -333,6 +340,7 @@ async def edit_food(request: Request, db: Session = Depends(get_db),
|
||||
food.sodium = sodium
|
||||
food.calcium = calcium
|
||||
food.source = source
|
||||
food.brand = brand
|
||||
|
||||
db.commit()
|
||||
return {"status": "success", "message": "Food updated successfully"}
|
||||
@@ -450,7 +458,7 @@ async def search_openfoodfacts(query: str, limit: int = 10):
|
||||
'calcium': get_nutrient_per_serving('calcium_100g', 0), # in mg
|
||||
'source': 'openfoodfacts',
|
||||
'openfoodfacts_id': product.get('code', ''),
|
||||
'brand': brands,
|
||||
'brand': brands, # Brand is already extracted
|
||||
'image_url': product.get('image_url', ''),
|
||||
'categories': product.get('categories', ''),
|
||||
'ingredients_text': product.get('ingredients_text_en', product.get('ingredients_text', ''))
|
||||
@@ -542,7 +550,7 @@ async def get_openfoodfacts_product(barcode: str):
|
||||
'calcium': get_nutrient_per_serving('calcium_100g', 0),
|
||||
'source': 'openfoodfacts',
|
||||
'openfoodfacts_id': barcode,
|
||||
'brand': brands,
|
||||
'brand': brands, # Brand is already extracted
|
||||
'image_url': product.get('image_url', ''),
|
||||
'categories': product.get('categories', ''),
|
||||
'ingredients_text': product.get('ingredients_text_en', product.get('ingredients_text', ''))
|
||||
@@ -635,15 +643,16 @@ async def add_openfoodfacts_food(request: Request, db: Session = Depends(get_db)
|
||||
name=display_name,
|
||||
serving_size=serving_size,
|
||||
serving_unit=serving_unit,
|
||||
calories=calories,
|
||||
protein=protein,
|
||||
carbs=carbs,
|
||||
calories=calories,
|
||||
protein=protein,
|
||||
carbs=carbs,
|
||||
fat=fat,
|
||||
fiber=fiber,
|
||||
sugar=sugar,
|
||||
sodium=sodium,
|
||||
fiber=fiber,
|
||||
sugar=sugar,
|
||||
sodium=sodium,
|
||||
calcium=calcium,
|
||||
source="openfoodfacts"
|
||||
source="openfoodfacts",
|
||||
brand=brand # Add brand here
|
||||
)
|
||||
db.add(food)
|
||||
db.commit()
|
||||
@@ -753,10 +762,11 @@ async def bulk_upload_meals(file: UploadFile = File(...), db: Session = Depends(
|
||||
|
||||
@app.post("/meals/add")
|
||||
async def add_meal(request: Request, db: Session = Depends(get_db),
|
||||
name: str = Form(...), meal_type: str = Form(...)):
|
||||
name: str = Form(...), meal_type: str = Form(...),
|
||||
meal_time: str = Form(...)):
|
||||
|
||||
try:
|
||||
meal = Meal(name=name, meal_type=meal_type)
|
||||
meal = Meal(name=name, meal_type=meal_type, meal_time=meal_time)
|
||||
db.add(meal)
|
||||
db.commit()
|
||||
db.refresh(meal)
|
||||
@@ -767,8 +777,8 @@ async def add_meal(request: Request, db: Session = Depends(get_db),
|
||||
|
||||
@app.post("/meals/edit")
|
||||
async def edit_meal(request: Request, db: Session = Depends(get_db),
|
||||
meal_id: int = Form(...), name: str = Form(...),
|
||||
meal_type: str = Form(...)):
|
||||
meal_id: int = Form(...), name: str = Form(...),
|
||||
meal_type: str = Form(...), meal_time: str = Form(...)):
|
||||
|
||||
try:
|
||||
meal = db.query(Meal).filter(Meal.id == meal_id).first()
|
||||
@@ -777,6 +787,7 @@ async def edit_meal(request: Request, db: Session = Depends(get_db),
|
||||
|
||||
meal.name = name
|
||||
meal.meal_type = meal_type
|
||||
meal.meal_time = meal_time # Update meal_time
|
||||
|
||||
db.commit()
|
||||
return {"status": "success", "message": "Meal updated successfully"}
|
||||
@@ -784,6 +795,24 @@ async def edit_meal(request: Request, db: Session = Depends(get_db),
|
||||
db.rollback()
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
@app.get("/meals/{meal_id}")
|
||||
async def get_meal_details(meal_id: int, db: Session = Depends(get_db)):
|
||||
"""Get details for a single meal"""
|
||||
try:
|
||||
meal = db.query(Meal).filter(Meal.id == meal_id).first()
|
||||
if not meal:
|
||||
return {"status": "error", "message": "Meal not found"}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"id": meal.id,
|
||||
"name": meal.name,
|
||||
"meal_type": meal.meal_type,
|
||||
"meal_time": meal.meal_time
|
||||
}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
@app.get("/meals/{meal_id}/foods")
|
||||
async def get_meal_foods(meal_id: int, db: Session = Depends(get_db)):
|
||||
"""Get all foods in a meal"""
|
||||
@@ -901,18 +930,45 @@ async def plan_page(request: Request, person: str = "Person A", week_start_date:
|
||||
})
|
||||
|
||||
@app.post("/plan/add")
|
||||
async def add_to_plan(request: Request, person: str = Form(...),
|
||||
plan_date: str = Form(...), meal_id: int = Form(...),
|
||||
meal_time: str = Form("Breakfast"), db: Session = Depends(get_db)):
|
||||
async def add_to_plan(request: Request, person: str = Form(None),
|
||||
plan_date: str = Form(None), meal_id: str = Form(None),
|
||||
meal_time: str = Form(None), db: Session = Depends(get_db)):
|
||||
|
||||
print(f"DEBUG: add_to_plan called with person={person}, plan_date={plan_date}, meal_id={meal_id}, meal_time={meal_time}")
|
||||
|
||||
# Validate required fields
|
||||
if not person or not plan_date or not meal_id or not meal_time:
|
||||
missing = []
|
||||
if not person: missing.append("person")
|
||||
if not plan_date: missing.append("plan_date")
|
||||
if not meal_id: missing.append("meal_id")
|
||||
if not meal_time: missing.append("meal_time")
|
||||
print(f"DEBUG: Missing required fields: {missing}")
|
||||
return {"status": "error", "message": f"Missing required fields: {', '.join(missing)}"}
|
||||
|
||||
try:
|
||||
from datetime import datetime
|
||||
plan_date_obj = datetime.fromisoformat(plan_date).date()
|
||||
plan = Plan(person=person, date=plan_date_obj, meal_id=meal_id, meal_time=meal_time)
|
||||
print(f"DEBUG: parsed plan_date_obj={plan_date_obj}")
|
||||
|
||||
meal_id_int = int(meal_id)
|
||||
|
||||
# Check if meal exists
|
||||
meal = db.query(Meal).filter(Meal.id == meal_id_int).first()
|
||||
if not meal:
|
||||
print(f"DEBUG: Meal with id {meal_id_int} not found")
|
||||
return {"status": "error", "message": f"Meal with id {meal_id_int} not found"}
|
||||
|
||||
plan = Plan(person=person, date=plan_date_obj, meal_id=meal_id_int, meal_time=meal_time)
|
||||
db.add(plan)
|
||||
db.commit()
|
||||
print(f"DEBUG: Successfully added plan")
|
||||
return {"status": "success"}
|
||||
except ValueError as e:
|
||||
print(f"DEBUG: ValueError: {str(e)}")
|
||||
return {"status": "error", "message": f"Invalid data: {str(e)}"}
|
||||
except Exception as e:
|
||||
print(f"DEBUG: Exception in add_to_plan: {str(e)}")
|
||||
db.rollback()
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
@@ -923,16 +979,21 @@ async def get_day_plan(person: str, date: str, db: Session = Depends(get_db)):
|
||||
from datetime import datetime
|
||||
plan_date = datetime.fromisoformat(date).date()
|
||||
plans = db.query(Plan).filter(Plan.person == person, Plan.date == plan_date).all()
|
||||
result = []
|
||||
|
||||
meal_details = []
|
||||
for plan in plans:
|
||||
result.append({
|
||||
meal_details.append({
|
||||
"id": plan.id,
|
||||
"meal_id": plan.meal_id,
|
||||
"meal_name": plan.meal.name,
|
||||
"meal_type": plan.meal.meal_type,
|
||||
"meal_time": plan.meal_time
|
||||
})
|
||||
return result
|
||||
|
||||
# Calculate daily totals using the same logic as plan_page
|
||||
day_totals = calculate_day_nutrition(plans, db)
|
||||
|
||||
return {"meals": meal_details, "day_totals": day_totals}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
@@ -953,6 +1014,7 @@ async def update_day_plan(request: Request, person: str = Form(...),
|
||||
|
||||
# Add new plans
|
||||
for meal_id in meal_id_list:
|
||||
# For now, assign a default meal_time. This will be refined later.
|
||||
plan = Plan(person=person, date=plan_date, meal_id=meal_id, meal_time="Breakfast")
|
||||
db.add(plan)
|
||||
|
||||
@@ -1193,7 +1255,7 @@ async def use_template(template_id: int, person: str = Form(...),
|
||||
# Copy all template meals to the specified date
|
||||
for tm in template_meals:
|
||||
print(f"DEBUG: Adding meal {tm.meal_id} ({tm.meal.name}) for {tm.meal_time}")
|
||||
plan = Plan(person=person, date=start_date_obj, meal_id=tm.meal_id)
|
||||
plan = Plan(person=person, date=start_date_obj, meal_id=tm.meal_id, meal_time=tm.meal_time) # Use meal_time from template
|
||||
db.add(plan)
|
||||
|
||||
db.commit()
|
||||
|
||||
@@ -26,6 +26,16 @@ def migrate_database():
|
||||
cursor.execute("ALTER TABLE foods ADD COLUMN source TEXT DEFAULT 'manual'")
|
||||
print("✓ Added source column to foods table")
|
||||
|
||||
# Check if meal_time column exists in meals table
|
||||
cursor.execute("PRAGMA table_info(meals)")
|
||||
columns = cursor.fetchall()
|
||||
column_names = [col[1] for col in columns]
|
||||
|
||||
if 'meal_time' not in column_names:
|
||||
print("Adding 'meal_time' column to meals table...")
|
||||
cursor.execute("ALTER TABLE meals ADD COLUMN meal_time TEXT DEFAULT 'Breakfast'")
|
||||
print("✓ Added meal_time column to meals table")
|
||||
|
||||
# Check if meal_time column exists in plans table
|
||||
cursor.execute("PRAGMA table_info(plans)")
|
||||
columns = cursor.fetchall()
|
||||
|
||||
60
migrate_food_brand.py
Normal file
60
migrate_food_brand.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Float
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker, Session
|
||||
import re
|
||||
|
||||
DATABASE_URL = "sqlite:///./meal_planner.db"
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
Base = declarative_base()
|
||||
|
||||
class Food(Base):
|
||||
__tablename__ = "foods"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String, unique=True, index=True)
|
||||
brand = Column(String, default="") # New field
|
||||
|
||||
def get_db():
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
def migrate_food_brands():
|
||||
db = next(get_db())
|
||||
foods = db.query(Food).all()
|
||||
|
||||
updated_count = 0
|
||||
for food in foods:
|
||||
# Check if the name contains a brand in parentheses
|
||||
match = re.search(r'\s*\((\w[^)]*)\)$', food.name)
|
||||
if match:
|
||||
brand_name = match.group(1).strip()
|
||||
# If brand is found and not already set, update
|
||||
if not food.brand and brand_name:
|
||||
food.brand = brand_name
|
||||
# Optionally remove brand from name
|
||||
food.name = re.sub(r'\s*\((\w[^)]*)\)$', '', food.name).strip()
|
||||
updated_count += 1
|
||||
print(f"Updated food '{food.name}' with brand '{food.brand}'")
|
||||
|
||||
db.commit()
|
||||
print(f"Migration complete. Updated {updated_count} food brands.")
|
||||
db.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Starting food brand migration...")
|
||||
# This will add the 'brand' column if it doesn't exist.
|
||||
# Note: For SQLite, ALTER TABLE ADD COLUMN is limited.
|
||||
# If the column already exists and you're just populating, Base.metadata.create_all() is fine.
|
||||
# If you're adding a new column to an existing table, you might need Alembic for proper migrations.
|
||||
# For this task, we'll assume the column is added manually or via a previous step.
|
||||
# Base.metadata.create_all(bind=engine) # This line should only be run if the table/column is new and not yet in DB
|
||||
|
||||
# We need to reflect the existing table schema to ensure the 'brand' column is known
|
||||
# by SQLAlchemy before attempting to set its value.
|
||||
# For a real-world scenario, a proper migration tool like Alembic would handle schema changes.
|
||||
# For this simplified example, we assume the 'brand' column already exists in the DB or will be added manually.
|
||||
|
||||
migrate_food_brands()
|
||||
@@ -365,6 +365,18 @@ function addFoodToMeal() {
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Meal Time</label>
|
||||
<select class="form-control" name="meal_time" required>
|
||||
<option value="Breakfast">Breakfast</option>
|
||||
<option value="Lunch">Lunch</option>
|
||||
<option value="Dinner">Dinner</option>
|
||||
<option value="Snack 1">Snack 1</option>
|
||||
<option value="Snack 2">Snack 2</option>
|
||||
<option value="Beverage 1">Beverage 1</option>
|
||||
<option value="Beverage 2">Beverage 2</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<tr>
|
||||
<th><input type="checkbox" id="select-all-meals" onclick="toggleAllMeals(this)"></th>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Food Items</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
@@ -23,7 +22,6 @@
|
||||
<tr>
|
||||
<td><input type="checkbox" name="selected_meals" value="{{ meal.id }}"></td>
|
||||
<td>{{ meal.name }}</td>
|
||||
<td>{{ meal.meal_type.title() }}</td>
|
||||
<td>
|
||||
{% if meal.meal_foods %}
|
||||
<ul class="list-unstyled">
|
||||
@@ -37,11 +35,11 @@
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary"
|
||||
onclick="editMeal({{ meal.id }}, '{{ meal.name }}', '{{ meal.meal_type }}')">
|
||||
onclick="editMeal({{ meal.id }})">
|
||||
<i class="bi bi-pencil"></i> Edit
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-success"
|
||||
onclick="manageMealFoods({{ meal.id }}, '{{ meal.name }}')">
|
||||
onclick="manageMealFoods({{ meal.id }})">
|
||||
<i class="bi bi-list"></i> Foods
|
||||
</button>
|
||||
</td>
|
||||
@@ -71,13 +69,16 @@
|
||||
<input type="text" class="form-control" name="name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Meal Type</label>
|
||||
<select class="form-control" name="meal_type" required>
|
||||
<option value="breakfast">Breakfast</option>
|
||||
<option value="lunch">Lunch</option>
|
||||
<option value="dinner">Dinner</option>
|
||||
<option value="snack">Snack</option>
|
||||
<option value="custom">Custom</option>
|
||||
<label class="form-label">Meal Time</label>
|
||||
<select class="form-control" name="meal_time" required>
|
||||
<option value="Breakfast">Breakfast</option>
|
||||
<option value="Lunch">Lunch</option>
|
||||
<option value="Dinner">Dinner</option>
|
||||
<option value="Snack 1">Snack 1</option>
|
||||
<option value="Snack 2">Snack 2</option>
|
||||
<option value="Beverage 1">Beverage 1</option>
|
||||
<option value="Beverage 2">Beverage 2</option>
|
||||
<option value="Custom">Custom</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
@@ -106,13 +107,16 @@
|
||||
<input type="text" class="form-control" name="name" id="edit_meal_name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Meal Type</label>
|
||||
<select class="form-control" name="meal_type" id="edit_meal_type" required>
|
||||
<option value="breakfast">Breakfast</option>
|
||||
<option value="lunch">Lunch</option>
|
||||
<option value="dinner">Dinner</option>
|
||||
<option value="snack">Snack</option>
|
||||
<option value="custom">Custom</option>
|
||||
<label class="form-label">Meal Time</label>
|
||||
<select class="form-control" name="meal_time" id="edit_meal_time" required>
|
||||
<option value="Breakfast">Breakfast</option>
|
||||
<option value="Lunch">Lunch</option>
|
||||
<option value="Dinner">Dinner</option>
|
||||
<option value="Snack 1">Snack 1</option>
|
||||
<option value="Snack 2">Snack 2</option>
|
||||
<option value="Beverage 1">Beverage 1</option>
|
||||
<option value="Beverage 2">Beverage 2</option>
|
||||
<option value="Custom">Custom</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
@@ -177,23 +181,44 @@ function toggleAllMeals(source) {
|
||||
}
|
||||
|
||||
// Edit meal function
|
||||
function editMeal(id, name, meal_type) {
|
||||
async function editMeal(id) {
|
||||
document.getElementById('edit_meal_id').value = id;
|
||||
document.getElementById('edit_meal_name').value = name;
|
||||
document.getElementById('edit_meal_type').value = meal_type;
|
||||
|
||||
new bootstrap.Modal(document.getElementById('editMealModal')).show();
|
||||
|
||||
try {
|
||||
const response = await fetch(`/meals/${id}`); // Assuming an endpoint /meals/{id} exists to get meal details
|
||||
const meal = await response.json();
|
||||
|
||||
if (meal.status === 'success') {
|
||||
document.getElementById('edit_meal_name').value = meal.name;
|
||||
document.getElementById('edit_meal_time').value = meal.meal_time;
|
||||
new bootstrap.Modal(document.getElementById('editMealModal')).show();
|
||||
} else {
|
||||
alert('Error fetching meal details: ' + meal.message);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Error fetching meal details: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Manage meal foods function
|
||||
async function manageMealFoods(mealId, mealName) {
|
||||
async function manageMealFoods(mealId) {
|
||||
document.getElementById('meal_id_for_food').value = mealId;
|
||||
document.getElementById('manageMealFoodsModalLabel').textContent = `Manage Foods for: ${mealName}`;
|
||||
|
||||
// Load current meal foods
|
||||
await loadCurrentMealFoods(mealId);
|
||||
|
||||
new bootstrap.Modal(document.getElementById('manageMealFoodsModal')).show();
|
||||
|
||||
try {
|
||||
const response = await fetch(`/meals/${mealId}`); // Assuming an endpoint /meals/{id} exists to get meal details
|
||||
const meal = await response.json();
|
||||
|
||||
if (meal.status === 'success') {
|
||||
document.getElementById('manageMealFoodsModalLabel').textContent = `Manage Foods for: ${meal.name}`;
|
||||
// Load current meal foods
|
||||
await loadCurrentMealFoods(mealId);
|
||||
new bootstrap.Modal(document.getElementById('manageMealFoodsModal')).show();
|
||||
} else {
|
||||
alert('Error fetching meal details: ' + meal.message);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Error fetching meal details: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Load current meal foods
|
||||
|
||||
@@ -42,10 +42,25 @@
|
||||
<small class="text-muted">{{ day.display }}</small>
|
||||
</td>
|
||||
<td>
|
||||
{% set meals_by_time = {} %}
|
||||
{% for plan in plans[day.date.isoformat()] %}
|
||||
<span class="badge bg-secondary me-1" title="{{ plan.meal_time }}">
|
||||
<small>{{ plan.meal_time }}:</small> {{ plan.meal.name }}
|
||||
</span>
|
||||
{% if plan.meal_time not in meals_by_time %}
|
||||
{% set _ = meals_by_time.update({plan.meal_time: []}) %}
|
||||
{% endif %}
|
||||
{% set _ = meals_by_time[plan.meal_time].append(plan) %}
|
||||
{% endfor %}
|
||||
|
||||
{% for meal_time in ["Breakfast", "Lunch", "Dinner", "Snack 1", "Snack 2", "Beverage 1", "Beverage 2"] %}
|
||||
<div class="mb-1">
|
||||
<strong>{{ meal_time }}:</strong>
|
||||
{% if meals_by_time[meal_time] %}
|
||||
{% for plan in meals_by_time[meal_time] %}
|
||||
<span class="badge bg-info me-1">{{ plan.meal.name }}</span>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<em class="text-muted">No meals</em>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not plans[day.date.isoformat()] %}
|
||||
<em class="text-muted">No meals</em>
|
||||
@@ -80,14 +95,14 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="quickAddMealForm">
|
||||
<input type="hidden" id="quickAddPlanDay" name="plan_day">
|
||||
<input type="hidden" id="quickAddPlanDay" name="plan_date">
|
||||
<input type="hidden" name="person" value="{{ person }}">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Select Meal</label>
|
||||
<select class="form-control" name="meal_id" required>
|
||||
<option value="">Choose meal...</option>
|
||||
{% for meal in meals %}
|
||||
<option value="{{ meal.id }}">{{ meal.name }} ({{ meal.meal_type }})</option>
|
||||
<option value="{{ meal.id }}">{{ meal.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -132,12 +147,12 @@
|
||||
<h6><i class="bi bi-plus-circle"></i> Add Meal</h6>
|
||||
<form id="addMealToDayForm">
|
||||
<input type="hidden" id="editDayPerson" name="person">
|
||||
<input type="hidden" id="editDayValue" name="plan_day">
|
||||
<input type="hidden" id="editDayValue" name="plan_date">
|
||||
<div class="mb-3">
|
||||
<select class="form-control" id="mealSelectForDay" name="meal_id" required>
|
||||
<option value="">Choose meal...</option>
|
||||
{% for meal in meals %}
|
||||
<option value="{{ meal.id }}">{{ meal.name }} ({{ meal.meal_type }})</option>
|
||||
<option value="{{ meal.id }}">{{ meal.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -247,7 +262,9 @@ async function editDay(date, person) {
|
||||
async function loadCurrentDayMeals(person, date) {
|
||||
try {
|
||||
const response = await fetch(`/plan/${person}/${date}`);
|
||||
const meals = await response.json();
|
||||
const data = await response.json(); // Now data contains both meals and day_totals
|
||||
const meals = data.meals;
|
||||
const dayTotals = data.day_totals;
|
||||
|
||||
const container = document.getElementById('currentDayMeals');
|
||||
if (meals.length === 0) {
|
||||
@@ -255,7 +272,7 @@ async function loadCurrentDayMeals(person, date) {
|
||||
} else {
|
||||
container.innerHTML = meals.map(meal => `
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded">
|
||||
<span><strong>${meal.meal_name}</strong> <small class="text-muted">(${meal.meal_type})</small></span>
|
||||
<span><strong>${meal.meal_time}:</strong> ${meal.meal_name}</span>
|
||||
<button class="btn btn-sm btn-outline-danger" onclick="removeMealFromDay(${meal.id})">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
@@ -263,8 +280,8 @@ async function loadCurrentDayMeals(person, date) {
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Update nutrition preview
|
||||
updateDayNutritionPreview(meals);
|
||||
// Update nutrition preview with actual totals
|
||||
updateDayNutritionPreview(dayTotals);
|
||||
} catch (error) {
|
||||
console.error('Error loading day meals:', error);
|
||||
}
|
||||
@@ -319,16 +336,29 @@ async function removeMealFromDay(planId) {
|
||||
}
|
||||
|
||||
// Update nutrition preview (simplified - you could enhance this with actual calculations)
|
||||
function updateDayNutritionPreview(meals) {
|
||||
function updateDayNutritionPreview(dayTotals) {
|
||||
const preview = document.getElementById('dayNutritionPreview');
|
||||
preview.innerHTML = `
|
||||
<div class="text-center">
|
||||
<div class="mb-2">
|
||||
<strong>${meals.length}</strong> meals planned
|
||||
<div class="row text-center">
|
||||
<div class="col-6 mb-2">
|
||||
<strong>${dayTotals.calories ? dayTotals.calories.toFixed(0) : 0}</strong><br>
|
||||
<small>Calories</small>
|
||||
</div>
|
||||
<div class="col-6 mb-2">
|
||||
<strong>${dayTotals.protein ? dayTotals.protein.toFixed(1) : 0}g</strong><br>
|
||||
<small>Protein (${dayTotals.protein_pct ? dayTotals.protein_pct.toFixed(1) : 0}%)</small>
|
||||
</div>
|
||||
<div class="col-6 mb-2">
|
||||
<strong>${dayTotals.carbs ? dayTotals.carbs.toFixed(1) : 0}g</strong><br>
|
||||
<small>Carbs (${dayTotals.carbs_pct ? dayTotals.carbs_pct.toFixed(1) : 0}%)</small>
|
||||
</div>
|
||||
<div class="col-6 mb-2">
|
||||
<strong>${dayTotals.fat ? dayTotals.fat.toFixed(1) : 0}g</strong><br>
|
||||
<small>Fat (${dayTotals.fat_pct ? dayTotals.fat_pct.toFixed(1) : 0}%)</small>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<strong>Net Carbs:</strong> ${dayTotals.net_carbs ? dayTotals.net_carbs.toFixed(1) : 0}g
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
Nutrition totals will be calculated when page reloads
|
||||
</small>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user