starting templates

This commit is contained in:
2025-09-19 14:19:40 -07:00
parent 4d75d19f8d
commit 0e3d30a287
5 changed files with 70 additions and 170 deletions

Binary file not shown.

41
main.py
View File

@@ -192,6 +192,11 @@ def calculate_day_nutrition(plans, db: Session):
async def root(request: Request): async def root(request: Request):
return templates.TemplateResponse("index.html", {"request": request}) return templates.TemplateResponse("index.html", {"request": request})
# Imports tab
@app.get("/imports", response_class=HTMLResponse)
async def imports_page(request: Request):
return templates.TemplateResponse("imports.html", {"request": request})
# Foods tab # Foods tab
@app.get("/foods", response_class=HTMLResponse) @app.get("/foods", response_class=HTMLResponse)
async def foods_page(request: Request, db: Session = Depends(get_db)): async def foods_page(request: Request, db: Session = Depends(get_db)):
@@ -601,37 +606,13 @@ async def remove_from_plan(plan_id: int, db: Session = Depends(get_db)):
db.rollback() db.rollback()
return {"status": "error", "message": str(e)} return {"status": "error", "message": str(e)}
# Detailed planner tab
@app.get("/detailed", response_class=HTMLResponse) @app.get("/detailed", response_class=HTMLResponse)
async def detailed_page(request: Request, person: str = "Person A", async def detailed(request: Request):
plan_day: str = None, db: Session = Depends(get_db)): return templates.TemplateResponse("detailed.html", {"request": request, "title": "Detailed"})
if not plan_day: @app.get("/templates", response_class=HTMLResponse)
plan_day = "Day1" async def templates_page(request: Request):
return templates.TemplateResponse("plans.html", {"request": request, "title": "Templates"})
# Get all plans for the selected day
plans = db.query(Plan).filter(Plan.person == person, Plan.date == plan_day).all()
# Group by meal type and calculate nutrition
meal_details = []
for plan in plans:
meal_nutrition = calculate_meal_nutrition(plan.meal, db)
meal_details.append({
'plan': plan,
'nutrition': meal_nutrition,
'foods': plan.meal.meal_foods
})
# Calculate day totals
day_totals = calculate_day_nutrition(plans, db)
# Create list of all days for the selector
days = [f"Day{i}" for i in range(1, 15)]
return templates.TemplateResponse("detailed.html", {
"request": request, "person": person, "selected_day": plan_day,
"meal_details": meal_details, "day_totals": day_totals, "days": days
})
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn

View File

@@ -67,15 +67,21 @@
<i class="bi bi-egg-fried"></i> Meals <i class="bi bi-egg-fried"></i> Meals
</button> </button>
</li> </li>
<li class="nav-item" role="presentation">
<button class="nav-link" onclick="location.href='/imports'">
<i class="bi bi-upload"></i> Imports
</button>
</li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" onclick="location.href='/plan'"> <button class="nav-link" onclick="location.href='/plan'">
<i class="bi bi-calendar-week"></i> Plan <i class="bi bi-calendar-week"></i> Plan
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item">
<button class="nav-link" onclick="location.href='/detailed'"> <a class="nav-link" href="/detailed">Detailed</a>
<i class="bi bi-calendar-day"></i> Detailed </li>
</button> <li class="nav-item">
<a class="nav-link" href="/templates">Templates</a>
</li> </li>
</ul> </ul>

View File

@@ -1,31 +1,11 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-12">
<h3>Bulk Import</h3> <button type="button" class="btn btn-primary btn-lg mb-4" data-bs-toggle="modal" data-bs-target="#addFoodModal">
<form action="/foods/upload" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label class="form-label">CSV File</label>
<input type="file" class="form-control" name="file" accept=".csv" required>
</div>
<button type="submit" class="btn btn-secondary mb-4">Upload CSV</button>
</form>
<button type="button" class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#addFoodModal">
<i class="bi bi-plus-circle"></i> Add New Food <i class="bi bi-plus-circle"></i> Add New Food
</button> </button>
</div>
<div class="col-md-8" id="upload-results" style="display: none;">
<div class="alert alert-success">
<strong>Upload Results:</strong>
<span id="created-count"></span> created,
<span id="updated-count"></span> updated
<div id="error-list" class="mt-2 text-danger"></div>
</div>
</div>
<div class="col-md-8">
<h3>Foods Database</h3> <h3>Foods Database</h3>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped"> <table class="table table-striped">
@@ -59,7 +39,7 @@
<td>{{ "%.2f"|format(food.calcium) }}mg</td> <td>{{ "%.2f"|format(food.calcium) }}mg</td>
<td> <td>
<button type="button" class="btn btn-sm btn-outline-primary" <button type="button" class="btn btn-sm btn-outline-primary"
onclick="editFood({{ food.id }}, '{{ food.name }}', '{{ food.serving_size }}', '{{ food.serving_unit }}', {{ food.calories }}, {{ food.protein }}, {{ food.carbs }}, {{ food.fat }}, {{ food.fiber }}, {{ food.sugar }}, {{ food.sodium }}, {{ food.calcium }})"> onclick="editFood({{ food.id }})">
<i class="bi bi-pencil"></i> Edit <i class="bi bi-pencil"></i> Edit
</button> </button>
</td> </td>
@@ -229,21 +209,10 @@
<script> <script>
// Edit food function // Edit food function
function editFood(id, name, serving_size, serving_unit, calories, protein, carbs, fat, fiber, sugar, sodium, calcium) { function editFood(foodId) {
document.getElementById('edit_food_id').value = id; // This function will be implemented after page load using the food data
document.getElementById('edit_name').value = name; // For now, we'll use a placeholder that will be replaced by the actual implementation
document.getElementById('edit_serving_size').value = serving_size; console.log("Edit food called for ID:", foodId);
document.getElementById('edit_serving_unit').value = serving_unit;
document.getElementById('edit_calories').value = calories;
document.getElementById('edit_protein').value = protein;
document.getElementById('edit_carbs').value = carbs;
document.getElementById('edit_fat').value = fat;
document.getElementById('edit_fiber').value = fiber;
document.getElementById('edit_sugar').value = sugar;
document.getElementById('edit_sodium').value = sodium;
document.getElementById('edit_calcium').value = calcium;
new bootstrap.Modal(document.getElementById('editFoodModal')).show();
} }
// Submit food form (add or edit) // Submit food form (add or edit)
@@ -302,50 +271,46 @@ async function deleteSelectedFoods() {
} }
} }
// CSV upload handling // Initialize editFood function after page load
document.querySelector('form[action="/foods/upload"]').addEventListener('submit', async (e) => { document.addEventListener('DOMContentLoaded', function() {
e.preventDefault(); const foodsData = {
const form = e.target; {% for food in foods %}
const submitBtn = form.querySelector('button[type="submit"]'); {{ food.id }}: {
const resultsDiv = document.getElementById('upload-results'); name: '{{ food.name | replace("'", "\\'") }}',
serving_size: '{{ food.serving_size | replace("'", "\\'") }}',
submitBtn.disabled = true; serving_unit: '{{ food.serving_unit | replace("'", "\\'") }}',
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status"></span> Uploading...'; calories: {{ food.calories }},
protein: {{ food.protein }},
try { carbs: {{ food.carbs }},
const formData = new FormData(form); fat: {{ food.fat }},
const response = await fetch('/foods/upload', { fiber: {{ food.fiber }},
method: 'POST', sugar: {{ food.sugar }},
body: formData sodium: {{ food.sodium }},
}); calcium: {{ food.calcium }}
},
{% endfor %}
};
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); // Implement the actual editFood function
window.editFood = function(foodId) {
const food = foodsData[foodId];
if (!food) return;
const results = await response.json(); document.getElementById('edit_food_id').value = foodId;
document.getElementById('edit_name').value = food.name;
resultsDiv.style.display = 'block'; document.getElementById('edit_serving_size').value = food.serving_size;
document.getElementById('created-count').textContent = results.created || 0; document.getElementById('edit_serving_unit').value = food.serving_unit;
document.getElementById('updated-count').textContent = results.updated || 0; document.getElementById('edit_calories').value = food.calories;
document.getElementById('edit_protein').value = food.protein;
if (results.errors?.length > 0) { document.getElementById('edit_carbs').value = food.carbs;
document.getElementById('error-list').innerHTML = document.getElementById('edit_fat').value = food.fat;
`<strong>Errors (${results.errors.length}):</strong><br>` + results.errors.join('<br>'); document.getElementById('edit_fiber').value = food.fiber;
} else { document.getElementById('edit_sugar').value = food.sugar;
document.getElementById('error-list').innerHTML = ''; document.getElementById('edit_sodium').value = food.sodium;
} document.getElementById('edit_calcium').value = food.calcium;
if (results.created || results.updated) { new bootstrap.Modal(document.getElementById('editFoodModal')).show();
setTimeout(() => window.location.reload(), 2000); };
}
} catch (error) {
resultsDiv.style.display = 'block';
resultsDiv.querySelector('.alert').className = 'alert alert-danger';
document.getElementById('error-list').innerHTML =
`<strong>Upload Failed:</strong> ${error.message}`;
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Upload CSV';
}
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -1,22 +1,11 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-12">
<h3>Bulk Import Meals</h3> <button type="button" class="btn btn-primary btn-lg mb-4" data-bs-toggle="modal" data-bs-target="#addMealModal">
<form action="/meals/upload" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label class="form-label">CSV File</label>
<input type="file" class="form-control" name="file" accept=".csv" required>
</div>
<button type="submit" class="btn btn-secondary mb-4">Upload CSV</button>
</form>
<button type="button" class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#addMealModal">
<i class="bi bi-plus-circle"></i> Create New Meal <i class="bi bi-plus-circle"></i> Create New Meal
</button> </button>
</div>
<div class="col-md-8">
<h3>Meals</h3> <h3>Meals</h3>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped"> <table class="table table-striped">
@@ -336,46 +325,5 @@ async function deleteSelectedMeals() {
} }
} }
// Meal CSV upload handling
document.querySelector('form[action="/meals/upload"]').addEventListener('submit', async (e) => {
e.preventDefault();
const form = e.target;
const submitBtn = form.querySelector('button[type="submit"]');
const resultsDiv = document.createElement('div');
resultsDiv.className = 'alert alert-info mt-3';
form.parentNode.insertBefore(resultsDiv, form.nextSibling);
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Uploading...';
try {
const formData = new FormData(form);
const response = await fetch('/meals/upload', {
method: 'POST',
body: formData
});
const results = await response.json();
if (results.errors?.length > 0) {
resultsDiv.className = 'alert alert-danger';
resultsDiv.innerHTML = `<strong>Errors (${results.errors.length}):</strong><br>` +
results.errors.join('<br>');
} else {
resultsDiv.className = 'alert alert-success';
resultsDiv.innerHTML = `Successfully created ${results.created} meals, updated ${results.updated}`;
}
if (results.created || results.updated) {
setTimeout(() => window.location.reload(), 2000);
}
} catch (error) {
resultsDiv.className = 'alert alert-danger';
resultsDiv.textContent = `Upload failed: ${error.message}`;
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Upload CSV';
}
});
</script> </script>
{% endblock %} {% endblock %}